1 # Copyright (c) 2012 OpenStack Foundation.
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
19 from oslo_config import cfg
20 from oslo_utils import uuidutils
23 from neutron._i18n import _
24 from neutron.api import extensions
25 from neutron.api.v2 import attributes as attr
26 from neutron.api.v2 import base
27 from neutron.common import constants as const
28 from neutron.common import exceptions as nexception
29 from neutron import manager
30 from neutron.quota import resource_registry
33 # Security group Exceptions
34 class SecurityGroupInvalidPortRange(nexception.InvalidInput):
35 message = _("For TCP/UDP protocols, port_range_min must be "
39 class SecurityGroupInvalidPortValue(nexception.InvalidInput):
40 message = _("Invalid value for port %(port)s")
43 class SecurityGroupInvalidIcmpValue(nexception.InvalidInput):
44 message = _("Invalid value for ICMP %(field)s (%(attr)s) "
45 "%(value)s. It must be 0 to 255.")
48 class SecurityGroupEthertypeConflictWithProtocol(nexception.InvalidInput):
49 message = _("Invalid ethertype %(ethertype)s for protocol "
53 class SecurityGroupMissingIcmpType(nexception.InvalidInput):
54 message = _("ICMP code (port-range-max) %(value)s is provided"
55 " but ICMP type (port-range-min) is missing.")
58 class SecurityGroupInUse(nexception.InUse):
59 message = _("Security Group %(id)s %(reason)s.")
61 def __init__(self, **kwargs):
62 if 'reason' not in kwargs:
63 kwargs['reason'] = _("in use")
64 super(SecurityGroupInUse, self).__init__(**kwargs)
67 class SecurityGroupCannotRemoveDefault(nexception.InUse):
68 message = _("Insufficient rights for removing default security group.")
71 class SecurityGroupCannotUpdateDefault(nexception.InUse):
72 message = _("Updating default security group not allowed.")
75 class SecurityGroupDefaultAlreadyExists(nexception.InUse):
76 message = _("Default security group already exists.")
79 class SecurityGroupRuleInvalidProtocol(nexception.InvalidInput):
80 message = _("Security group rule protocol %(protocol)s not supported. "
81 "Only protocol values %(values)s and integer representations "
82 "[0 to 255] are supported.")
85 class SecurityGroupRulesNotSingleTenant(nexception.InvalidInput):
86 message = _("Multiple tenant_ids in bulk security group rule create"
90 class SecurityGroupRemoteGroupAndRemoteIpPrefix(nexception.InvalidInput):
91 message = _("Only remote_ip_prefix or remote_group_id may "
95 class SecurityGroupProtocolRequiredWithPorts(nexception.InvalidInput):
96 message = _("Must also specifiy protocol if port range is given.")
99 class SecurityGroupNotSingleGroupRules(nexception.InvalidInput):
100 message = _("Only allowed to update rules for "
101 "one security profile at a time")
104 class SecurityGroupNotFound(nexception.NotFound):
105 message = _("Security group %(id)s does not exist")
108 class SecurityGroupRuleNotFound(nexception.NotFound):
109 message = _("Security group rule %(id)s does not exist")
112 class DuplicateSecurityGroupRuleInPost(nexception.InUse):
113 message = _("Duplicate Security Group Rule in POST.")
116 class SecurityGroupRuleExists(nexception.InUse):
117 message = _("Security group rule already exists. Rule id is %(id)s.")
120 class SecurityGroupRuleInUse(nexception.InUse):
121 message = _("Security Group Rule %(id)s %(reason)s.")
123 def __init__(self, **kwargs):
124 if 'reason' not in kwargs:
125 kwargs['reason'] = _("in use")
126 super(SecurityGroupRuleInUse, self).__init__(**kwargs)
129 class SecurityGroupRuleParameterConflict(nexception.InvalidInput):
130 message = _("Conflicting value ethertype %(ethertype)s for CIDR %(cidr)s")
133 class SecurityGroupConflict(nexception.Conflict):
134 message = _("Error %(reason)s while attempting the operation.")
137 class SecurityGroupRuleInvalidEtherType(nexception.InvalidInput):
138 message = _("Security group rule for ethertype '%(ethertype)s' not "
139 "supported. Allowed values are %(values)s.")
142 def convert_protocol(value):
147 if val >= 0 and val <= 255:
148 # Set value of protocol number to string due to bug 1381379,
149 # PostgreSQL fails when it tries to compare integer with string,
152 raise SecurityGroupRuleInvalidProtocol(
153 protocol=value, values=sg_supported_protocols)
154 except (ValueError, TypeError):
155 if value.lower() in sg_supported_protocols:
157 raise SecurityGroupRuleInvalidProtocol(
158 protocol=value, values=sg_supported_protocols)
159 except AttributeError:
160 raise SecurityGroupRuleInvalidProtocol(
161 protocol=value, values=sg_supported_protocols)
164 def convert_ethertype_to_case_insensitive(value):
165 if isinstance(value, six.string_types):
166 for ethertype in sg_supported_ethertypes:
167 if ethertype.lower() == value.lower():
169 raise SecurityGroupRuleInvalidEtherType(
170 ethertype=value, values=sg_supported_ethertypes)
173 def convert_validate_port_value(port):
178 except (ValueError, TypeError):
179 raise SecurityGroupInvalidPortValue(port=port)
181 if val >= 0 and val <= 65535:
184 raise SecurityGroupInvalidPortValue(port=port)
187 def convert_to_uuid_list_or_none(value_list):
188 if value_list is None:
190 for sg_id in value_list:
191 if not uuidutils.is_uuid_like(sg_id):
192 msg = _("'%s' is not an integer or uuid") % sg_id
193 raise nexception.InvalidInput(error_message=msg)
197 def convert_ip_prefix_to_cidr(ip_prefix):
201 cidr = netaddr.IPNetwork(ip_prefix)
203 except (ValueError, TypeError, netaddr.AddrFormatError):
204 raise nexception.InvalidCIDR(input=ip_prefix)
207 def _validate_name_not_default(data, valid_values=None):
208 if data.lower() == "default":
209 raise SecurityGroupDefaultAlreadyExists()
212 attr.validators['type:name_not_default'] = _validate_name_not_default
214 sg_supported_protocols = [None, const.PROTO_NAME_TCP, const.PROTO_NAME_UDP,
215 const.PROTO_NAME_ICMP, const.PROTO_NAME_ICMP_V6]
216 sg_supported_ethertypes = ['IPv4', 'IPv6']
219 RESOURCE_ATTRIBUTE_MAP = {
221 'id': {'allow_post': False, 'allow_put': False,
222 'validate': {'type:uuid': None},
224 'primary_key': True},
225 'name': {'allow_post': True, 'allow_put': True,
226 'is_visible': True, 'default': '',
227 'validate': {'type:name_not_default': attr.NAME_MAX_LEN}},
228 'description': {'allow_post': True, 'allow_put': True,
229 'validate': {'type:string': attr.DESCRIPTION_MAX_LEN},
230 'is_visible': True, 'default': ''},
231 'tenant_id': {'allow_post': True, 'allow_put': False,
232 'required_by_policy': True,
233 'validate': {'type:string': attr.TENANT_ID_MAX_LEN},
235 'security_group_rules': {'allow_post': False, 'allow_put': False,
238 'security_group_rules': {
239 'id': {'allow_post': False, 'allow_put': False,
240 'validate': {'type:uuid': None},
242 'primary_key': True},
243 'security_group_id': {'allow_post': True, 'allow_put': False,
244 'is_visible': True, 'required_by_policy': True},
245 'remote_group_id': {'allow_post': True, 'allow_put': False,
246 'default': None, 'is_visible': True},
247 'direction': {'allow_post': True, 'allow_put': False,
249 'validate': {'type:values': ['ingress', 'egress']}},
250 'protocol': {'allow_post': True, 'allow_put': False,
251 'is_visible': True, 'default': None,
252 'convert_to': convert_protocol},
253 'port_range_min': {'allow_post': True, 'allow_put': False,
254 'convert_to': convert_validate_port_value,
255 'default': None, 'is_visible': True},
256 'port_range_max': {'allow_post': True, 'allow_put': False,
257 'convert_to': convert_validate_port_value,
258 'default': None, 'is_visible': True},
259 'ethertype': {'allow_post': True, 'allow_put': False,
260 'is_visible': True, 'default': 'IPv4',
261 'convert_to': convert_ethertype_to_case_insensitive,
262 'validate': {'type:values': sg_supported_ethertypes}},
263 'remote_ip_prefix': {'allow_post': True, 'allow_put': False,
264 'default': None, 'is_visible': True,
265 'convert_to': convert_ip_prefix_to_cidr},
266 'tenant_id': {'allow_post': True, 'allow_put': False,
267 'required_by_policy': True,
268 'validate': {'type:string': attr.TENANT_ID_MAX_LEN},
274 SECURITYGROUPS = 'security_groups'
275 EXTENDED_ATTRIBUTES_2_0 = {
276 'ports': {SECURITYGROUPS: {'allow_post': True,
279 'convert_to': convert_to_uuid_list_or_none,
280 'default': attr.ATTR_NOT_SPECIFIED}}}
281 security_group_quota_opts = [
282 cfg.IntOpt('quota_security_group',
284 help=_('Number of security groups allowed per tenant. '
285 'A negative value means unlimited.')),
286 cfg.IntOpt('quota_security_group_rule',
288 help=_('Number of security rules allowed per tenant. '
289 'A negative value means unlimited.')),
291 cfg.CONF.register_opts(security_group_quota_opts, 'QUOTAS')
294 class Securitygroup(extensions.ExtensionDescriptor):
295 """Security group extension."""
299 return "security-group"
303 return "security-group"
306 def get_description(cls):
307 return "The security groups extension."
310 def get_updated(cls):
311 return "2012-10-05T10:00:00-00:00"
314 def get_resources(cls):
315 """Returns Ext Resources."""
316 my_plurals = [(key, key[:-1]) for key in RESOURCE_ATTRIBUTE_MAP.keys()]
317 attr.PLURALS.update(dict(my_plurals))
319 plugin = manager.NeutronManager.get_plugin()
320 for resource_name in ['security_group', 'security_group_rule']:
321 collection_name = resource_name.replace('_', '-') + "s"
322 params = RESOURCE_ATTRIBUTE_MAP.get(resource_name + "s", dict())
323 resource_registry.register_resource_by_name(resource_name)
324 controller = base.create_resource(collection_name,
326 plugin, params, allow_bulk=True,
327 allow_pagination=True,
330 ex = extensions.ResourceExtension(collection_name,
337 def get_extended_resources(self, version):
339 return dict(list(EXTENDED_ATTRIBUTES_2_0.items()) +
340 list(RESOURCE_ATTRIBUTE_MAP.items()))
345 @six.add_metaclass(abc.ABCMeta)
346 class SecurityGroupPluginBase(object):
349 def create_security_group(self, context, security_group):
353 def update_security_group(self, context, id, security_group):
357 def delete_security_group(self, context, id):
361 def get_security_groups(self, context, filters=None, fields=None,
362 sorts=None, limit=None, marker=None,
367 def get_security_group(self, context, id, fields=None):
371 def create_security_group_rule(self, context, security_group_rule):
375 def delete_security_group_rule(self, context, id):
379 def get_security_group_rules(self, context, filters=None, fields=None,
380 sorts=None, limit=None, marker=None,
385 def get_security_group_rule(self, context, id, fields=None):