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):
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 :return: the dictionary object
75 out['name'] = self.name
77 out['description'] = self.description
79 project = keystone_utils.get_project(
80 keystone=keystone, project_name=self.project_name)
83 project_id = project.id
85 out['tenant_id'] = project_id
87 raise SecurityGroupConfigError(
88 'Could not find project ID for project named - ' +
91 return {'security_group': out}
94 class Direction(enum.Enum):
102 class Protocol(enum.Enum):
132 class Ethertype(enum.Enum):
140 class SecurityGroupConfigError(Exception):
142 Exception to be thrown when security group settings attributes are
147 class SecurityGroupRuleConfig(object):
149 Class representing a keypair configuration
152 def __init__(self, **kwargs):
154 Constructor - all parameters are optional
155 :param sec_grp_name: The security group's name on which to add the
157 :param description: The rule's description
158 :param direction: An enumeration of type
159 create_security_group.RULE_DIRECTION (required)
160 :param remote_group_id: The group ID to associate with this rule
161 (this should be changed to group name once
162 snaps support Groups) (optional)
163 :param protocol: An enumeration of type
164 create_security_group.RULE_PROTOCOL or a string value
165 that will be mapped accordingly (optional)
166 :param ethertype: An enumeration of type
167 create_security_group.RULE_ETHERTYPE (optional)
168 :param port_range_min: The minimum port number in the range that is
169 matched by the security group rule. When the
170 protocol is TCP or UDP, this value must be <=
172 :param port_range_max: The maximum port number in the range that is
173 matched by the security group rule. When the
174 protocol is TCP or UDP, this value must be <=
176 :param remote_ip_prefix: The remote IP prefix to associate with this
177 metering rule packet (optional)
179 TODO - Need to support the tenant...
182 self.description = kwargs.get('description')
183 self.sec_grp_name = kwargs.get('sec_grp_name')
184 self.remote_group_id = kwargs.get('remote_group_id')
185 self.direction = None
186 if kwargs.get('direction'):
187 self.direction = map_direction(kwargs['direction'])
190 if kwargs.get('protocol'):
191 self.protocol = map_protocol(kwargs['protocol'])
193 self.protocol = Protocol.null
195 self.ethertype = None
196 if kwargs.get('ethertype'):
197 self.ethertype = map_ethertype(kwargs['ethertype'])
199 self.port_range_min = kwargs.get('port_range_min')
200 self.port_range_max = kwargs.get('port_range_max')
201 self.remote_ip_prefix = kwargs.get('remote_ip_prefix')
203 if not self.direction or not self.sec_grp_name:
204 raise SecurityGroupRuleConfigError(
205 'direction and sec_grp_name are required')
207 def dict_for_neutron(self, neutron, keystone, project_name):
209 Returns a dictionary object representing this object.
210 This is meant to be converted into JSON designed for use by the Neutron
212 :param neutron: the neutron client for performing lookups
213 :param keystone: the keystone client for performing lookups
214 :param project_name: the name of the project associated with the group
215 :return: the dictionary object
220 out['description'] = self.description
222 out['direction'] = self.direction.name
223 if self.port_range_min:
224 out['port_range_min'] = self.port_range_min
225 if self.port_range_max:
226 out['port_range_max'] = self.port_range_max
228 out['ethertype'] = self.ethertype.name
229 if self.protocol and self.protocol.value != 'null':
230 out['protocol'] = self.protocol.value
231 if self.sec_grp_name:
232 sec_grp = neutron_utils.get_security_group(
233 neutron, keystone, sec_grp_name=self.sec_grp_name,
234 project_name=project_name)
236 out['security_group_id'] = sec_grp.id
238 raise SecurityGroupRuleConfigError(
239 'Cannot locate security group with name - ' +
241 if self.remote_group_id:
242 out['remote_group_id'] = self.remote_group_id
243 if self.remote_ip_prefix:
244 out['remote_ip_prefix'] = self.remote_ip_prefix
246 return {'security_group_rule': out}
248 def rule_eq(self, rule):
250 Returns True if this setting created the rule
251 :param rule: the rule to evaluate
254 if self.description is not None:
255 if rule.description is not None and rule.description != '':
257 elif self.description != rule.description:
258 if rule.description != '':
261 if self.direction.name != rule.direction:
264 if self.ethertype and rule.ethertype:
265 if self.ethertype.name != rule.ethertype:
268 if self.port_range_min and rule.port_range_min:
269 if self.port_range_min != rule.port_range_min:
272 if self.port_range_max and rule.port_range_max:
273 if self.port_range_max != rule.port_range_max:
276 if self.protocol and rule.protocol:
277 if self.protocol.name != rule.protocol:
280 if self.remote_group_id and rule.remote_group_id:
281 if self.remote_group_id != rule.remote_group_id:
284 if self.remote_ip_prefix and rule.remote_ip_prefix:
285 if self.remote_ip_prefix != rule.remote_ip_prefix:
290 def __eq__(self, other):
292 self.description == other.description and
293 self.direction == other.direction and
294 self.port_range_min == other.port_range_min and
295 self.port_range_max == other.port_range_max and
296 self.ethertype == other.ethertype and
297 self.protocol == other.protocol and
298 self.sec_grp_name == other.sec_grp_name and
299 self.remote_group_id == other.remote_group_id and
300 self.remote_ip_prefix == other.remote_ip_prefix)
303 return hash((self.sec_grp_name, self.description, self.direction,
304 self.remote_group_id,
305 self.protocol, self.ethertype, self.port_range_min,
306 self.port_range_max, self.remote_ip_prefix))
309 def map_direction(direction):
311 Takes a the direction value maps it to the Direction enum. When None return
313 :param direction: the direction value
314 :return: the Direction enum object
315 :raise: Exception if value is invalid
317 if not direction or 'None' == str(direction):
319 if isinstance(direction, enum.Enum):
320 if direction.__class__.__name__ == 'Direction':
323 raise SecurityGroupRuleConfigError(
324 'Invalid class - ' + direction.__class__.__name__)
325 elif isinstance(direction, str):
326 dir_str = str(direction)
327 if dir_str == 'egress':
328 return Direction.egress
329 elif dir_str == 'ingress':
330 return Direction.ingress
332 raise SecurityGroupRuleConfigError(
333 'Invalid Direction - ' + dir_str)
335 return map_direction(str(direction))
338 def map_protocol(protocol):
340 Takes a the protocol value maps it to the Protocol enum. When None return
342 :param protocol: the protocol value
343 :return: the Protocol enum object
344 :raise: Exception if value is invalid
348 elif isinstance(protocol, enum.Enum):
349 if protocol.__class__.__name__ == 'Protocol':
352 raise SecurityGroupRuleConfigError(
353 'Invalid class - ' + protocol.__class__.__name__)
354 elif isinstance(protocol, str) or isinstance(protocol, int):
355 for proto_enum in Protocol:
356 if proto_enum.name == protocol or proto_enum.value == protocol:
357 if proto_enum == Protocol.any:
360 raise SecurityGroupRuleConfigError(
361 'Invalid Protocol - ' + str(protocol))
363 return map_protocol(str(protocol))
366 def map_ethertype(ethertype):
368 Takes a the ethertype value maps it to the Ethertype enum. When None return
370 :param ethertype: the ethertype value
371 :return: the Ethertype enum object
372 :raise: Exception if value is invalid
374 if not ethertype or 'None' == str(ethertype):
376 elif isinstance(ethertype, enum.Enum):
377 if ethertype.__class__.__name__ == 'Ethertype':
380 raise SecurityGroupRuleConfigError(
381 'Invalid class - ' + ethertype.__class__.__name__)
382 elif isinstance(ethertype, str) or isinstance(ethertype, int):
383 eth_str = str(ethertype)
384 if eth_str == 'IPv6' or eth_str == '6':
385 return Ethertype.IPv6
386 elif eth_str == 'IPv4' or eth_str == '4':
387 return Ethertype.IPv4
389 raise SecurityGroupRuleConfigError(
390 'Invalid Ethertype - ' + eth_str)
392 return map_ethertype(str(ethertype))
395 class SecurityGroupRuleConfigError(Exception):
397 Exception to be thrown when security group rule settings attributes are