761
781
return (inst_ret_list, reservation_id)
763
def ensure_default_security_group(self, context):
764
"""Ensure that a context has a security group.
766
Creates a security group for the security context if it does not
769
:param context: the security context
772
self.db.security_group_get_by_name(context,
775
except exception.NotFound:
776
values = {'name': 'default',
777
'description': 'default',
778
'user_id': context.user_id,
779
'project_id': context.project_id}
780
self.db.security_group_create(context, values)
782
def trigger_security_group_rules_refresh(self, context, security_group_id):
783
"""Called when a rule is added to or removed from a security_group."""
785
security_group = self.db.security_group_get(context, security_group_id)
788
for instance in security_group['instances']:
789
if instance['host'] is not None:
790
hosts.add(instance['host'])
793
self.compute_rpcapi.refresh_security_group_rules(context,
794
security_group.id, host=host)
796
def trigger_security_group_members_refresh(self, context, group_ids):
797
"""Called when a security group gains a new or loses a member.
799
Sends an update request to each compute node for whom this is
802
# First, we get the security group rules that reference these groups as
804
security_group_rules = set()
805
for group_id in group_ids:
806
security_group_rules.update(
807
self.db.security_group_rule_get_by_security_group_grantee(
811
# ..then we distill the security groups to which they belong..
812
security_groups = set()
813
for rule in security_group_rules:
814
security_group = self.db.security_group_get(
816
rule['parent_group_id'])
817
security_groups.add(security_group)
819
# ..then we find the instances that are members of these groups..
821
for security_group in security_groups:
822
for instance in security_group['instances']:
823
instances.add(instance)
825
# ...then we find the hosts where they live...
827
for instance in instances:
829
hosts.add(instance['host'])
831
# ...and finally we tell these nodes to refresh their view of this
832
# particular security group.
834
self.compute_rpcapi.refresh_security_group_members(context,
837
783
def trigger_provider_fw_rules_refresh(self, context):
838
784
"""Called when a rule is added/removed from a provider firewall"""
840
786
hosts = [x['host'] for (x, idx)
841
787
in self.db.service_get_all_compute_sorted(context)]
842
788
for host in hosts:
844
self.db.queue_get_for(context, FLAGS.compute_topic, host),
845
{'method': 'refresh_provider_fw_rules', 'args': {}})
847
def _is_security_group_associated_with_server(self, security_group,
849
"""Check if the security group is already associated
850
with the instance. If Yes, return True.
853
if not security_group:
856
instances = security_group.get('instances')
860
for inst in instances:
861
if (instance_uuid == inst['uuid']):
867
def add_security_group(self, context, instance, security_group_name):
868
"""Add security group to the instance"""
869
security_group = self.db.security_group_get_by_name(context,
873
instance_uuid = instance['uuid']
875
#check if the security group is associated with the server
876
if self._is_security_group_associated_with_server(security_group,
878
raise exception.SecurityGroupExistsForInstance(
879
security_group_id=security_group['id'],
880
instance_id=instance_uuid)
882
#check if the instance is in running state
883
if instance['power_state'] != power_state.RUNNING:
884
raise exception.InstanceNotRunning(instance_id=instance_uuid)
886
self.db.instance_add_security_group(context.elevated(),
888
security_group['id'])
889
# NOTE(comstud): No instance_uuid argument to this compute manager
891
self.compute_rpcapi.refresh_security_group_rules(context,
892
security_group['id'], host=instance['host'])
895
def remove_security_group(self, context, instance, security_group_name):
896
"""Remove the security group associated with the instance"""
897
security_group = self.db.security_group_get_by_name(context,
901
instance_uuid = instance['uuid']
903
#check if the security group is associated with the server
904
if not self._is_security_group_associated_with_server(security_group,
906
raise exception.SecurityGroupNotExistsForInstance(
907
security_group_id=security_group['id'],
908
instance_id=instance_uuid)
910
#check if the instance is in running state
911
if instance['power_state'] != power_state.RUNNING:
912
raise exception.InstanceNotRunning(instance_id=instance_uuid)
914
self.db.instance_remove_security_group(context.elevated(),
916
security_group['id'])
917
# NOTE(comstud): No instance_uuid argument to this compute manager
919
self.compute_rpcapi.refresh_security_group_rules(context,
920
security_group['id'], host=instance['host'])
789
self.compute_rpcapi.refresh_provider_fw_rules(context, host)
922
791
@wrap_check_policy
923
792
def update(self, context, instance, **kwargs):
2036
1889
'fingerprint': key_pair['fingerprint'],
1894
class SecurityGroupAPI(base.Base):
1896
Sub-set of the Compute API related to managing security groups
1897
and security group rules
1899
def __init__(self, **kwargs):
1900
super(SecurityGroupAPI, self).__init__(**kwargs)
1901
self.security_group_rpcapi = compute_rpcapi.SecurityGroupAPI()
1902
self.sgh = importutils.import_object(FLAGS.security_group_handler)
1904
def validate_property(self, value, property, allowed):
1906
Validate given security group property.
1908
:param value: the value to validate, as a string or unicode
1909
:param property: the property, either 'name' or 'description'
1910
:param allowed: the range of characters allowed
1915
except AttributeError:
1916
msg = _("Security group %s is not a string or unicode") % property
1917
self.raise_invalid_property(msg)
1919
msg = _("Security group %s cannot be empty.") % property
1920
self.raise_invalid_property(msg)
1922
if allowed and not re.match(allowed, val):
1923
# Some validation to ensure that values match API spec.
1924
# - Alphanumeric characters, spaces, dashes, and underscores.
1925
# TODO(Daviey): LP: #813685 extend beyond group_name checking, and
1926
# probably create a param validator that can be used elsewhere.
1927
msg = (_("Value (%(value)s) for parameter Group%(property)s is "
1928
"invalid. Content limited to '%(allowed)'.") %
1929
dict(value=value, allowed=allowed,
1930
property=property.capitalize()))
1931
self.raise_invalid_property(msg)
1933
msg = _("Security group %s should not be greater "
1934
"than 255 characters.") % property
1935
self.raise_invalid_property(msg)
1937
def ensure_default(self, context):
1938
"""Ensure that a context has a security group.
1940
Creates a security group for the security context if it does not
1943
:param context: the security context
1946
self.db.security_group_get_by_name(context,
1949
except exception.NotFound:
1950
values = {'name': 'default',
1951
'description': 'default',
1952
'user_id': context.user_id,
1953
'project_id': context.project_id}
1954
self.db.security_group_create(context, values)
1956
def create(self, context, name, description):
1958
reservations = QUOTAS.reserve(context, security_groups=1)
1959
except exception.OverQuota:
1960
msg = _("Quota exceeded, too many security groups.")
1961
self.raise_over_quota(msg)
1963
LOG.audit(_("Create Security Group %s"), name, context=context)
1965
self.ensure_default(context)
1967
if self.db.security_group_exists(context, context.project_id, name):
1968
msg = _('Security group %s already exists') % name
1969
self.raise_group_already_exists(msg)
1972
group = {'user_id': context.user_id,
1973
'project_id': context.project_id,
1975
'description': description}
1976
group_ref = self.db.security_group_create(context, group)
1977
self.sgh.trigger_security_group_create_refresh(context, group)
1978
# Commit the reservation
1979
QUOTAS.commit(context, reservations)
1981
with excutils.save_and_reraise_exception():
1982
QUOTAS.rollback(context, reservations)
1986
def get(self, context, name=None, id=None, map_exception=False):
1987
self.ensure_default(context)
1990
return self.db.security_group_get_by_name(context,
1994
return self.db.security_group_get(context, id)
1995
except exception.NotFound as exp:
1998
self.raise_not_found(msg)
2002
def list(self, context, names=None, ids=None, project=None):
2003
self.ensure_default(context)
2009
groups.append(self.db.security_group_get_by_name(context,
2014
groups.append(self.db.security_group_get(context, id))
2016
elif context.is_admin:
2017
groups = self.db.security_group_get_all(context)
2020
groups = self.db.security_group_get_by_project(context, project)
2024
def destroy(self, context, security_group):
2025
if self.db.security_group_in_use(context, security_group.id):
2026
msg = _("Security group is still in use")
2027
self.raise_invalid_group(msg)
2031
reservations = QUOTAS.reserve(context, security_groups=-1)
2034
LOG.exception(_("Failed to update usages deallocating "
2037
LOG.audit(_("Delete security group %s"), security_group.name,
2039
self.db.security_group_destroy(context, security_group.id)
2041
self.sgh.trigger_security_group_destroy_refresh(context,
2044
# Commit the reservations
2046
QUOTAS.commit(context, reservations)
2048
def is_associated_with_server(self, security_group, instance_uuid):
2049
"""Check if the security group is already associated
2050
with the instance. If Yes, return True.
2053
if not security_group:
2056
instances = security_group.get('instances')
2060
for inst in instances:
2061
if (instance_uuid == inst['uuid']):
2066
@wrap_check_security_groups_policy
2067
def add_to_instance(self, context, instance, security_group_name):
2068
"""Add security group to the instance"""
2069
security_group = self.db.security_group_get_by_name(context,
2071
security_group_name)
2073
instance_uuid = instance['uuid']
2075
#check if the security group is associated with the server
2076
if self.is_associated_with_server(security_group, instance_uuid):
2077
raise exception.SecurityGroupExistsForInstance(
2078
security_group_id=security_group['id'],
2079
instance_id=instance_uuid)
2081
#check if the instance is in running state
2082
if instance['power_state'] != power_state.RUNNING:
2083
raise exception.InstanceNotRunning(instance_id=instance_uuid)
2085
self.db.instance_add_security_group(context.elevated(),
2087
security_group['id'])
2088
params = {"security_group_id": security_group['id']}
2089
# NOTE(comstud): No instance_uuid argument to this compute manager
2091
self.security_group_rpcapi.refresh_security_group_rules(context,
2092
security_group['id'], host=instance['host'])
2094
self.trigger_handler('instance_add_security_group',
2095
context, instance, security_group_name)
2097
@wrap_check_security_groups_policy
2098
def remove_from_instance(self, context, instance, security_group_name):
2099
"""Remove the security group associated with the instance"""
2100
security_group = self.db.security_group_get_by_name(context,
2102
security_group_name)
2104
instance_uuid = instance['uuid']
2106
#check if the security group is associated with the server
2107
if not self.is_associated_with_server(security_group, instance_uuid):
2108
raise exception.SecurityGroupNotExistsForInstance(
2109
security_group_id=security_group['id'],
2110
instance_id=instance_uuid)
2112
#check if the instance is in running state
2113
if instance['power_state'] != power_state.RUNNING:
2114
raise exception.InstanceNotRunning(instance_id=instance_uuid)
2116
self.db.instance_remove_security_group(context.elevated(),
2118
security_group['id'])
2119
params = {"security_group_id": security_group['id']}
2120
# NOTE(comstud): No instance_uuid argument to this compute manager
2122
self.security_group_rpcapi.refresh_security_group_rules(context,
2123
security_group['id'], host=instance['host'])
2125
self.trigger_handler('instance_remove_security_group',
2126
context, instance, security_group_name)
2128
def trigger_handler(self, event, *args):
2129
handle = getattr(self.sgh, 'trigger_%s_refresh' % event)
2132
def trigger_rules_refresh(self, context, id):
2133
"""Called when a rule is added to or removed from a security_group."""
2135
security_group = self.db.security_group_get(context, id)
2138
for instance in security_group['instances']:
2139
if instance['host'] is not None:
2140
hosts.add(instance['host'])
2143
self.security_group_rpcapi.refresh_security_group_rules(context,
2144
security_group.id, host=host)
2146
def trigger_members_refresh(self, context, group_ids):
2147
"""Called when a security group gains a new or loses a member.
2149
Sends an update request to each compute node for whom this is
2152
# First, we get the security group rules that reference these groups as
2154
security_group_rules = set()
2155
for group_id in group_ids:
2156
security_group_rules.update(
2157
self.db.security_group_rule_get_by_security_group_grantee(
2161
# ..then we distill the security groups to which they belong..
2162
security_groups = set()
2163
for rule in security_group_rules:
2164
security_group = self.db.security_group_get(
2166
rule['parent_group_id'])
2167
security_groups.add(security_group)
2169
# ..then we find the instances that are members of these groups..
2171
for security_group in security_groups:
2172
for instance in security_group['instances']:
2173
instances.add(instance)
2175
# ...then we find the hosts where they live...
2177
for instance in instances:
2178
if instance['host']:
2179
hosts.add(instance['host'])
2181
# ...and finally we tell these nodes to refresh their view of this
2182
# particular security group.
2184
self.security_group_rpcapi.refresh_security_group_members(context,
2185
group_id, host=host)
2187
def parse_cidr(self, cidr):
2190
cidr = urllib.unquote(cidr).decode()
2191
except Exception as e:
2192
self.raise_invalid_cidr(cidr, e)
2194
if not utils.is_valid_cidr(cidr):
2195
self.raise_invalid_cidr(cidr)
2202
def new_group_ingress_rule(grantee_group_id, protocol, from_port,
2204
return SecurityGroupAPI._new_ingress_rule(protocol, from_port,
2205
to_port, group_id=grantee_group_id)
2208
def new_cidr_ingress_rule(grantee_cidr, protocol, from_port, to_port):
2209
return SecurityGroupAPI._new_ingress_rule(protocol, from_port,
2210
to_port, cidr=grantee_cidr)
2213
def _new_ingress_rule(ip_protocol, from_port, to_port,
2214
group_id=None, cidr=None):
2218
values['group_id'] = group_id
2219
# Open everything if an explicit port range or type/code are not
2220
# specified, but only if a source group was specified.
2221
ip_proto_upper = ip_protocol.upper() if ip_protocol else ''
2222
if (ip_proto_upper == 'ICMP' and
2223
from_port is None and to_port is None):
2226
elif (ip_proto_upper in ['TCP', 'UDP'] and from_port is None
2227
and to_port is None):
2232
values['cidr'] = cidr
2234
if ip_protocol and from_port is not None and to_port is not None:
2236
ip_protocol = str(ip_protocol)
2238
# Verify integer conversions
2239
from_port = int(from_port)
2240
to_port = int(to_port)
2242
if ip_protocol.upper() == 'ICMP':
2243
raise exception.InvalidInput(reason="Type and"
2244
" Code must be integers for ICMP protocol type")
2246
raise exception.InvalidInput(reason="To and From ports "
2249
if ip_protocol.upper() not in ['TCP', 'UDP', 'ICMP']:
2250
raise exception.InvalidIpProtocol(protocol=ip_protocol)
2252
# Verify that from_port must always be less than
2253
# or equal to to_port
2254
if (ip_protocol.upper() in ['TCP', 'UDP'] and
2255
(from_port > to_port)):
2256
raise exception.InvalidPortRange(from_port=from_port,
2257
to_port=to_port, msg="Former value cannot"
2258
" be greater than the later")
2260
# Verify valid TCP, UDP port ranges
2261
if (ip_protocol.upper() in ['TCP', 'UDP'] and
2262
(from_port < 1 or to_port > 65535)):
2263
raise exception.InvalidPortRange(from_port=from_port,
2264
to_port=to_port, msg="Valid TCP ports should"
2265
" be between 1-65535")
2267
# Verify ICMP type and code
2268
if (ip_protocol.upper() == "ICMP" and
2269
(from_port < -1 or from_port > 255 or
2270
to_port < -1 or to_port > 255)):
2271
raise exception.InvalidPortRange(from_port=from_port,
2272
to_port=to_port, msg="For ICMP, the"
2273
" type:code must be valid")
2275
values['protocol'] = ip_protocol
2276
values['from_port'] = from_port
2277
values['to_port'] = to_port
2280
# If cidr based filtering, protocol and ports are mandatory
2286
def rule_exists(self, security_group, values):
2287
"""Indicates whether the specified rule values are already
2288
defined in the given security group.
2290
for rule in security_group.rules:
2292
keys = ('group_id', 'cidr', 'from_port', 'to_port', 'protocol')
2294
if rule.get(key) != values.get(key):
2295
is_duplicate = False
2298
return rule.get('id') or True
2301
def get_rule(self, context, id):
2302
self.ensure_default(context)
2304
return self.db.security_group_rule_get(context, id)
2305
except exception.NotFound:
2306
msg = _("Rule (%s) not found") % id
2307
self.raise_not_found(msg)
2309
def add_rules(self, context, id, name, vals):
2310
count = QUOTAS.count(context, 'security_group_rules', id)
2312
projected = count + len(vals)
2313
QUOTAS.limit_check(context, security_group_rules=projected)
2314
except exception.OverQuota:
2315
msg = _("Quota exceeded, too many security group rules.")
2316
self.raise_over_quota(msg)
2318
msg = _("Authorize security group ingress %s")
2319
LOG.audit(msg, name, context=context)
2321
rules = [self.db.security_group_rule_create(context, v) for v in vals]
2323
self.trigger_rules_refresh(context, id=id)
2324
self.trigger_handler('security_group_rule_create', context,
2325
[r['id'] for r in rules])
2328
def remove_rules(self, context, security_group, rule_ids):
2329
msg = _("Revoke security group ingress %s")
2330
LOG.audit(msg, security_group['name'], context=context)
2332
for rule_id in rule_ids:
2333
self.db.security_group_rule_destroy(context, rule_id)
2335
# NOTE(vish): we removed some rules, so refresh
2336
self.trigger_rules_refresh(context, id=security_group['id'])
2337
self.trigger_handler('security_group_rule_destroy', context, rule_ids)
2340
def raise_invalid_property(msg):
2341
raise NotImplementedError()
2344
def raise_group_already_exists(msg):
2345
raise NotImplementedError()
2348
def raise_invalid_group(msg):
2349
raise NotImplementedError()
2352
def raise_invalid_cidr(cidr, decoding_exception=None):
2353
raise NotImplementedError()
2356
def raise_over_quota(msg):
2357
raise NotImplementedError()
2360
def raise_not_found(msg):
2361
raise NotImplementedError()