1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 # and others. All rights reserved.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain 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,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
17 from snaps.openstack.utils import keystone_utils, neutron_utils
20 class SecurityGroupConfig(object):
22 Class representing a keypair configuration
25 def __init__(self, **kwargs):
28 :param name: The security group's name (required)
29 :param description: The security group's description (optional)
30 :param project_name: The name of the project under which the security
32 :param rule_settings: a list of SecurityGroupRuleConfig objects
35 self.name = kwargs.get('name')
36 self.description = kwargs.get('description')
37 self.project_name = kwargs.get('project_name')
38 self.rule_settings = list()
40 rule_settings = kwargs.get('rules')
42 rule_settings = kwargs.get('rule_settings')
45 for rule_setting in rule_settings:
46 if isinstance(rule_setting, SecurityGroupRuleConfig):
47 self.rule_settings.append(rule_setting)
49 rule_setting['sec_grp_name'] = self.name
50 self.rule_settings.append(SecurityGroupRuleConfig(
54 raise SecurityGroupConfigError('The attribute name is required')
56 for rule_setting in self.rule_settings:
57 if rule_setting.sec_grp_name != self.name:
58 raise SecurityGroupConfigError(
59 'Rule settings must correspond with the name of this '
62 def dict_for_neutron(self, keystone, project_id):
64 Returns a dictionary object representing this object.
65 This is meant to be converted into JSON designed for use by the Neutron
68 TODO - expand automated testing to exercise all parameters
69 :param keystone: the Keystone client
70 :param project_id: the default project ID
71 :return: the dictionary object
76 out['name'] = self.name
78 out['description'] = self.description
80 project = keystone_utils.get_project(
81 keystone=keystone, project_name=self.project_name)
84 project_id = project.id
86 out['tenant_id'] = project_id
88 raise SecurityGroupConfigError(
89 'Could not find project ID for project named - ' +
92 out['tenant_id'] = project_id
94 return {'security_group': out}
97 class Direction(enum.Enum):
105 class Protocol(enum.Enum):
135 class Ethertype(enum.Enum):
143 class SecurityGroupConfigError(Exception):
145 Exception to be thrown when security group settings attributes are
150 class SecurityGroupRuleConfig(object):
152 Class representing a keypair configuration
155 def __init__(self, **kwargs):
157 Constructor - all parameters are optional
158 :param sec_grp_name: The security group's name on which to add the
160 :param description: The rule's description
161 :param direction: An enumeration of type
162 create_security_group.RULE_DIRECTION (required)
163 :param remote_group_id: The group ID to associate with this rule
164 (this should be changed to group name once
165 snaps support Groups) (optional)
166 :param protocol: An enumeration of type
167 create_security_group.RULE_PROTOCOL or a string value
168 that will be mapped accordingly (optional)
169 :param ethertype: An enumeration of type
170 create_security_group.RULE_ETHERTYPE (optional)
171 :param port_range_min: The minimum port number in the range that is
172 matched by the security group rule. When the
173 protocol is TCP or UDP, this value must be <=
175 :param port_range_max: The maximum port number in the range that is
176 matched by the security group rule. When the
177 protocol is TCP or UDP, this value must be <=
179 :param remote_ip_prefix: The remote IP prefix to associate with this
180 metering rule packet (optional)
182 TODO - Need to support the tenant...
185 self.description = kwargs.get('description')
186 self.sec_grp_name = kwargs.get('sec_grp_name')
187 self.remote_group_id = kwargs.get('remote_group_id')
188 self.direction = None
189 if kwargs.get('direction'):
190 self.direction = map_direction(kwargs['direction'])
193 if kwargs.get('protocol'):
194 self.protocol = map_protocol(kwargs['protocol'])
196 self.protocol = Protocol.null
198 self.ethertype = None
199 if kwargs.get('ethertype'):
200 self.ethertype = map_ethertype(kwargs['ethertype'])
202 self.port_range_min = kwargs.get('port_range_min')
203 self.port_range_max = kwargs.get('port_range_max')
204 self.remote_ip_prefix = kwargs.get('remote_ip_prefix')
206 if not self.direction or not self.sec_grp_name:
207 raise SecurityGroupRuleConfigError(
208 'direction and sec_grp_name are required')
210 def dict_for_neutron(self, neutron, project_id):
212 Returns a dictionary object representing this object.
213 This is meant to be converted into JSON designed for use by the Neutron
215 :param neutron: the neutron client for performing lookups
216 :param project_id: the ID of the project associated with the group
217 :return: the dictionary object
222 out['description'] = self.description
224 out['direction'] = self.direction.name
225 if self.port_range_min:
226 out['port_range_min'] = self.port_range_min
227 if self.port_range_max:
228 out['port_range_max'] = self.port_range_max
230 out['ethertype'] = self.ethertype.name
231 if self.protocol and self.protocol.value != 'null':
232 out['protocol'] = self.protocol.value
233 if self.sec_grp_name:
234 sec_grp = neutron_utils.get_security_group(
235 neutron, sec_grp_name=self.sec_grp_name, project_id=project_id)
237 out['security_group_id'] = sec_grp.id
239 raise SecurityGroupRuleConfigError(
240 'Cannot locate security group with name - ' +
242 if self.remote_group_id:
243 out['remote_group_id'] = self.remote_group_id
244 if self.remote_ip_prefix:
245 out['remote_ip_prefix'] = self.remote_ip_prefix
247 return {'security_group_rule': out}
249 def rule_eq(self, rule):
251 Returns True if this setting created the rule
252 :param rule: the rule to evaluate
255 if self.description is not None:
256 if rule.description is not None and rule.description != '':
258 elif self.description != rule.description:
259 if rule.description != '':
262 if self.direction.name != rule.direction:
265 if self.ethertype and rule.ethertype:
266 if self.ethertype.name != rule.ethertype:
269 if self.port_range_min and rule.port_range_min:
270 if self.port_range_min != rule.port_range_min:
273 if self.port_range_max and rule.port_range_max:
274 if self.port_range_max != rule.port_range_max:
277 if self.protocol and rule.protocol:
278 if self.protocol.name != rule.protocol:
281 if self.remote_group_id and rule.remote_group_id:
282 if self.remote_group_id != rule.remote_group_id:
285 if self.remote_ip_prefix and rule.remote_ip_prefix:
286 if self.remote_ip_prefix != rule.remote_ip_prefix:
291 def __eq__(self, other):
293 self.description == other.description and
294 self.direction == other.direction and
295 self.port_range_min == other.port_range_min and
296 self.port_range_max == other.port_range_max and
297 self.ethertype == other.ethertype and
298 self.protocol == other.protocol and
299 self.sec_grp_name == other.sec_grp_name and
300 self.remote_group_id == other.remote_group_id and
301 self.remote_ip_prefix == other.remote_ip_prefix)
304 return hash((self.sec_grp_name, self.description, self.direction,
305 self.remote_group_id,
306 self.protocol, self.ethertype, self.port_range_min,
307 self.port_range_max, self.remote_ip_prefix))
310 def map_direction(direction):
312 Takes a the direction value maps it to the Direction enum. When None return
314 :param direction: the direction value
315 :return: the Direction enum object
316 :raise: Exception if value is invalid
320 if isinstance(direction, Direction):
322 elif (isinstance(direction, str) or isinstance(direction, unicode)
323 or isinstance(direction, unicode)):
324 dir_str = str(direction)
325 if dir_str == 'egress':
326 return Direction.egress
327 elif dir_str == 'ingress':
328 return Direction.ingress
330 raise SecurityGroupRuleConfigError(
331 'Invalid Direction - ' + dir_str)
333 return map_direction(direction.value)
336 def map_protocol(protocol):
338 Takes a the protocol value maps it to the Protocol enum. When None return
340 :param protocol: the protocol value
341 :return: the Protocol enum object
342 :raise: Exception if value is invalid
346 elif isinstance(protocol, Protocol):
348 elif (isinstance(protocol, str) or isinstance(protocol, unicode)
349 or isinstance(protocol, int)):
350 for proto_enum in Protocol:
351 if proto_enum.name == protocol or proto_enum.value == protocol:
352 if proto_enum == Protocol.any:
355 raise SecurityGroupRuleConfigError(
356 'Invalid Protocol - ' + protocol)
358 return map_protocol(protocol.value)
361 def map_ethertype(ethertype):
363 Takes a the ethertype value maps it to the Ethertype enum. When None return
365 :param ethertype: the ethertype value
366 :return: the Ethertype enum object
367 :raise: Exception if value is invalid
371 elif isinstance(ethertype, Ethertype):
373 elif (isinstance(ethertype, str) or isinstance(ethertype, unicode)
374 or isinstance(ethertype, int)):
375 eth_str = str(ethertype)
376 if eth_str == 'IPv6' or eth_str == '6':
377 return Ethertype.IPv6
378 elif eth_str == 'IPv4' or eth_str == '4':
379 return Ethertype.IPv4
381 raise SecurityGroupRuleConfigError(
382 'Invalid Ethertype - ' + eth_str)
384 return map_ethertype(ethertype.value)
387 class SecurityGroupRuleConfigError(Exception):
389 Exception to be thrown when security group rule settings attributes are