Refactoring of SecurityGroupSettings to extend SecurityGroupConfig
[snaps.git] / snaps / openstack / create_security_group.py
1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 #                    and others.  All rights reserved.
3 #
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:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
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.
15 import logging
16
17 import enum
18 from neutronclient.common.exceptions import NotFound, Conflict
19
20 from snaps.config.security_group import (
21     SecurityGroupConfig, SecurityGroupRuleConfig)
22 from snaps.openstack.openstack_creator import OpenStackNetworkObject
23 from snaps.openstack.utils import keystone_utils
24 from snaps.openstack.utils import neutron_utils
25
26 __author__ = 'spisarski'
27
28 logger = logging.getLogger('OpenStackSecurityGroup')
29
30
31 class OpenStackSecurityGroup(OpenStackNetworkObject):
32     """
33     Class responsible for managing a Security Group in OpenStack
34     """
35
36     def __init__(self, os_creds, sec_grp_settings):
37         """
38         Constructor - all parameters are required
39         :param os_creds: The credentials to connect with OpenStack
40         :param sec_grp_settings: The settings used to create a security group
41         """
42         super(self.__class__, self).__init__(os_creds)
43
44         self.sec_grp_settings = sec_grp_settings
45
46         # Attributes instantiated on create()
47         self.__security_group = None
48
49         # dict where the rule settings object is the key
50         self.__rules = dict()
51
52     def initialize(self):
53         """
54         Loads existing security group.
55         :return: the security group domain object
56         """
57         super(self.__class__, self).initialize()
58
59         self.__security_group = neutron_utils.get_security_group(
60             self._neutron, sec_grp_settings=self.sec_grp_settings)
61         if self.__security_group:
62             # Populate rules
63             existing_rules = neutron_utils.get_rules_by_security_group(
64                 self._neutron, self.__security_group)
65
66             for existing_rule in existing_rules:
67                 # For Custom Rules
68                 rule_setting = self.__get_setting_from_rule(existing_rule)
69                 self.__rules[rule_setting] = existing_rule
70
71             self.__security_group = neutron_utils.get_security_group_by_id(
72                 self._neutron, self.__security_group.id)
73
74         return self.__security_group
75
76     def create(self):
77         """
78         Responsible for creating the security group.
79         :return: the security group domain object
80         """
81         self.initialize()
82
83         if not self.__security_group:
84             logger.info(
85                 'Creating security group %s...' % self.sec_grp_settings.name)
86
87             keystone = keystone_utils.keystone_client(self._os_creds)
88             self.__security_group = neutron_utils.create_security_group(
89                 self._neutron, keystone,
90                 self.sec_grp_settings)
91
92             # Get the rules added for free
93             auto_rules = neutron_utils.get_rules_by_security_group(
94                 self._neutron, self.__security_group)
95
96             ctr = 0
97             for auto_rule in auto_rules:
98                 auto_rule_setting = self.__generate_rule_setting(auto_rule)
99                 self.__rules[auto_rule_setting] = auto_rule
100                 ctr += 1
101
102             # Create the custom rules
103             for sec_grp_rule_setting in self.sec_grp_settings.rule_settings:
104                 try:
105                     custom_rule = neutron_utils.create_security_group_rule(
106                         self._neutron, sec_grp_rule_setting)
107                     self.__rules[sec_grp_rule_setting] = custom_rule
108                 except Conflict as e:
109                     logger.warn('Unable to create rule due to conflict - %s',
110                                 e)
111
112             # Refresh security group object to reflect the new rules added
113             self.__security_group = neutron_utils.get_security_group(
114                 self._neutron, sec_grp_settings=self.sec_grp_settings)
115
116         return self.__security_group
117
118     def __generate_rule_setting(self, rule):
119         """
120         Creates a SecurityGroupRuleConfig object for a given rule
121         :param rule: the rule from which to create the
122                     SecurityGroupRuleConfig object
123         :return: the newly instantiated SecurityGroupRuleConfig object
124         """
125         sec_grp = neutron_utils.get_security_group_by_id(
126             self._neutron, rule.security_group_id)
127
128         setting = SecurityGroupRuleConfig(
129             description=rule.description,
130             direction=rule.direction,
131             ethertype=rule.ethertype,
132             port_range_min=rule.port_range_min,
133             port_range_max=rule.port_range_max,
134             protocol=rule.protocol,
135             remote_group_id=rule.remote_group_id,
136             remote_ip_prefix=rule.remote_ip_prefix,
137             sec_grp_name=sec_grp.name)
138         return setting
139
140     def clean(self):
141         """
142         Removes and deletes the rules then the security group.
143         """
144         for setting, rule in self.__rules.items():
145             try:
146                 neutron_utils.delete_security_group_rule(self._neutron, rule)
147             except NotFound as e:
148                 logger.warning('Rule not found, cannot delete - ' + str(e))
149                 pass
150         self.__rules = dict()
151
152         if self.__security_group:
153             try:
154                 neutron_utils.delete_security_group(self._neutron,
155                                                     self.__security_group)
156             except NotFound as e:
157                 logger.warning(
158                     'Security Group not found, cannot delete - ' + str(e))
159
160             self.__security_group = None
161
162     def get_security_group(self):
163         """
164         Returns the OpenStack security group object
165         :return:
166         """
167         return self.__security_group
168
169     def get_rules(self):
170         """
171         Returns the associated rules
172         :return:
173         """
174         return self.__rules
175
176     def add_rule(self, rule_setting):
177         """
178         Adds a rule to this security group
179         :param rule_setting: the rule configuration
180         """
181         rule_setting.sec_grp_name = self.sec_grp_settings.name
182         new_rule = neutron_utils.create_security_group_rule(self._neutron,
183                                                             rule_setting)
184         self.__rules[rule_setting] = new_rule
185         self.sec_grp_settings.rule_settings.append(rule_setting)
186
187     def remove_rule(self, rule_id=None, rule_setting=None):
188         """
189         Removes a rule to this security group by id, name, or rule_setting
190         object
191         :param rule_id: the rule's id
192         :param rule_setting: the rule's setting object
193         """
194         rule_to_remove = None
195         if rule_id or rule_setting:
196             if rule_id:
197                 rule_to_remove = neutron_utils.get_rule_by_id(
198                     self._neutron, self.__security_group, rule_id)
199             elif rule_setting:
200                 rule_to_remove = self.__rules.get(rule_setting)
201
202         if rule_to_remove:
203             neutron_utils.delete_security_group_rule(self._neutron,
204                                                      rule_to_remove)
205             rule_setting = self.__get_setting_from_rule(rule_to_remove)
206             if rule_setting:
207                 self.__rules.pop(rule_setting)
208             else:
209                 logger.warning('Rule setting is None, cannot remove rule')
210
211     def __get_setting_from_rule(self, rule):
212         """
213         Returns the associated RuleSetting object for a given rule
214         :param rule: the Rule object
215         :return: the associated RuleSetting object or None
216         """
217         for rule_setting in self.sec_grp_settings.rule_settings:
218             if rule_setting.rule_eq(rule):
219                 return rule_setting
220         return None
221
222
223 class SecurityGroupSettings(SecurityGroupConfig):
224     """
225     Class to hold the configuration settings required for creating OpenStack
226     SecurityGroup objects
227     deprecated - use snaps.config.security_group.SecurityGroupConfig instead
228     """
229
230     def __init__(self, **kwargs):
231         from warnings import warn
232         warn('Use snaps.config.security_group.SecurityGroupConfig instead',
233              DeprecationWarning)
234         super(self.__class__, self).__init__(**kwargs)
235
236
237 class Direction(enum.Enum):
238     """
239     A rule's direction
240     deprecated - use snaps.config.security_group.Direction
241     """
242     ingress = 'ingress'
243     egress = 'egress'
244
245
246 class Protocol(enum.Enum):
247     """
248     A rule's protocol
249     deprecated - use snaps.config.security_group.Protocol
250     """
251     ah = 51
252     dccp = 33
253     egp = 8
254     esp = 50
255     gre = 47
256     icmp = 1
257     icmpv6 = 58
258     igmp = 2
259     ipv6_encap = 41
260     ipv6_frag = 44
261     ipv6_icmp = 58
262     ipv6_nonxt = 59
263     ipv6_opts = 60
264     ipv6_route = 43
265     ospf = 89
266     pgm = 113
267     rsvp = 46
268     sctp = 132
269     tcp = 6
270     udp = 17
271     udplite = 136
272     vrrp = 112
273     any = 'any'
274     null = 'null'
275
276
277 class Ethertype(enum.Enum):
278     """
279     A rule's ethertype
280     deprecated - use snaps.config.security_group.Ethertype
281     """
282     IPv4 = 4
283     IPv6 = 6
284
285
286 class SecurityGroupRuleSettings(SecurityGroupRuleConfig):
287     """
288     Class to hold the configuration settings required for creating OpenStack
289     SecurityGroupRule objects
290     deprecated - use snaps.config.security_group.SecurityGroupRuleConfig
291     instead
292     """
293
294     def __init__(self, **kwargs):
295         from warnings import warn
296         warn('Use snaps.config.security_group.SecurityGroupRuleConfig instead',
297              DeprecationWarning)
298         super(self.__class__, self).__init__(**kwargs)