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 project_name: the name of the project associated with the group
214 :return: the dictionary object
219 out['description'] = self.description
221 out['direction'] = self.direction.name
222 if self.port_range_min:
223 out['port_range_min'] = self.port_range_min
224 if self.port_range_max:
225 out['port_range_max'] = self.port_range_max
227 out['ethertype'] = self.ethertype.name
228 if self.protocol and self.protocol.value != 'null':
229 out['protocol'] = self.protocol.value
230 if self.sec_grp_name:
231 sec_grp = neutron_utils.get_security_group(
232 neutron, keystone, sec_grp_name=self.sec_grp_name,
233 project_name=project_name)
235 out['security_group_id'] = sec_grp.id
237 raise SecurityGroupRuleConfigError(
238 'Cannot locate security group with name - ' +
240 if self.remote_group_id:
241 out['remote_group_id'] = self.remote_group_id
242 if self.remote_ip_prefix:
243 out['remote_ip_prefix'] = self.remote_ip_prefix
245 return {'security_group_rule': out}
247 def rule_eq(self, rule):
249 Returns True if this setting created the rule
250 :param rule: the rule to evaluate
253 if self.description is not None:
254 if rule.description is not None and rule.description != '':
256 elif self.description != rule.description:
257 if rule.description != '':
260 if self.direction.name != rule.direction:
263 if self.ethertype and rule.ethertype:
264 if self.ethertype.name != rule.ethertype:
267 if self.port_range_min and rule.port_range_min:
268 if self.port_range_min != rule.port_range_min:
271 if self.port_range_max and rule.port_range_max:
272 if self.port_range_max != rule.port_range_max:
275 if self.protocol and rule.protocol:
276 if self.protocol.name != rule.protocol:
279 if self.remote_group_id and rule.remote_group_id:
280 if self.remote_group_id != rule.remote_group_id:
283 if self.remote_ip_prefix and rule.remote_ip_prefix:
284 if self.remote_ip_prefix != rule.remote_ip_prefix:
289 def __eq__(self, other):
291 self.description == other.description and
292 self.direction == other.direction and
293 self.port_range_min == other.port_range_min and
294 self.port_range_max == other.port_range_max and
295 self.ethertype == other.ethertype and
296 self.protocol == other.protocol and
297 self.sec_grp_name == other.sec_grp_name and
298 self.remote_group_id == other.remote_group_id and
299 self.remote_ip_prefix == other.remote_ip_prefix)
302 return hash((self.sec_grp_name, self.description, self.direction,
303 self.remote_group_id,
304 self.protocol, self.ethertype, self.port_range_min,
305 self.port_range_max, self.remote_ip_prefix))
308 def map_direction(direction):
310 Takes a the direction value maps it to the Direction enum. When None return
312 :param direction: the direction value
313 :return: the Direction enum object
314 :raise: Exception if value is invalid
318 if isinstance(direction, Direction):
320 elif (isinstance(direction, str) or isinstance(direction, unicode)
321 or isinstance(direction, unicode)):
322 dir_str = str(direction)
323 if dir_str == 'egress':
324 return Direction.egress
325 elif dir_str == 'ingress':
326 return Direction.ingress
328 raise SecurityGroupRuleConfigError(
329 'Invalid Direction - ' + dir_str)
331 return map_direction(direction.value)
334 def map_protocol(protocol):
336 Takes a the protocol value maps it to the Protocol enum. When None return
338 :param protocol: the protocol value
339 :return: the Protocol enum object
340 :raise: Exception if value is invalid
344 elif isinstance(protocol, Protocol):
346 elif (isinstance(protocol, str) or isinstance(protocol, unicode)
347 or isinstance(protocol, int)):
348 for proto_enum in Protocol:
349 if proto_enum.name == protocol or proto_enum.value == protocol:
350 if proto_enum == Protocol.any:
353 raise SecurityGroupRuleConfigError(
354 'Invalid Protocol - ' + protocol)
356 return map_protocol(protocol.value)
359 def map_ethertype(ethertype):
361 Takes a the ethertype value maps it to the Ethertype enum. When None return
363 :param ethertype: the ethertype value
364 :return: the Ethertype enum object
365 :raise: Exception if value is invalid
369 elif isinstance(ethertype, Ethertype):
371 elif (isinstance(ethertype, str) or isinstance(ethertype, unicode)
372 or isinstance(ethertype, int)):
373 eth_str = str(ethertype)
374 if eth_str == 'IPv6' or eth_str == '6':
375 return Ethertype.IPv6
376 elif eth_str == 'IPv4' or eth_str == '4':
377 return Ethertype.IPv4
379 raise SecurityGroupRuleConfigError(
380 'Invalid Ethertype - ' + eth_str)
382 return map_ethertype(ethertype.value)
385 class SecurityGroupRuleConfigError(Exception):
387 Exception to be thrown when security group rule settings attributes are