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