Browse Source

Group policy API-1 HEAT resources: EP, EPG, L2, L3

This is the first in a series of patches which implement
resources for neutron group policy APIs.

This patch implements four new resources:
    Endpoint
    EndpointGroup
    L2-Policy
    L3-Policy

Change-Id: Iac413aaa460bffa5c6f111e2e16beb9507a3d24a
Implements: blueprint group-based-policy-automation
Hemanth Ravi 4 years ago
parent
commit
a878e4080a

+ 0
- 0
gbpautomation/heat/engine/__init__.py View File


+ 0
- 0
gbpautomation/heat/engine/clients/__init__.py View File


+ 0
- 0
gbpautomation/heat/engine/clients/os/__init__.py View File


+ 60
- 0
gbpautomation/heat/engine/clients/os/grouppolicy.py View File

@@ -0,0 +1,60 @@
1
+#
2
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
3
+#    not use this file except in compliance with the License. You may obtain
4
+#    a copy of the License at
5
+#
6
+#         http://www.apache.org/licenses/LICENSE-2.0
7
+#
8
+#    Unless required by applicable law or agreed to in writing, software
9
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
+#    License for the specific language governing permissions and limitations
12
+#    under the License.
13
+
14
+from gbpclient.v2_0 import client as gbpc
15
+from neutronclient.common import exceptions
16
+
17
+from heat.engine.clients import client_plugin
18
+
19
+
20
+class GBPClientPlugin(client_plugin.ClientPlugin):
21
+
22
+    exceptions_module = exceptions
23
+
24
+    def _create(self):
25
+
26
+        con = self.context
27
+
28
+        endpoint_type = self._get_client_option('grouppolicy', 'endpoint_type')
29
+        endpoint = self.url_for(service_type='network',
30
+                                endpoint_type=endpoint_type)
31
+
32
+        args = {
33
+            'auth_url': con.auth_url,
34
+            'service_type': 'network',
35
+            'token': self.auth_token,
36
+            'endpoint_url': endpoint,
37
+            'endpoint_type': endpoint_type,
38
+            'ca_cert': self._get_client_option('grouppolicy', 'ca_file'),
39
+            'insecure': self._get_client_option('grouppolicy', 'insecure')
40
+        }
41
+
42
+        return gbpc.Client(**args)
43
+
44
+    def is_not_found(self, ex):
45
+        if isinstance(ex, (exceptions.NotFound,
46
+                           exceptions.NetworkNotFoundClient,
47
+                           exceptions.PortNotFoundClient)):
48
+            return True
49
+        return (isinstance(ex, exceptions.NeutronClientException) and
50
+                ex.status_code == 404)
51
+
52
+    def is_conflict(self, ex):
53
+        if not isinstance(ex, exceptions.NeutronClientException):
54
+            return False
55
+        return ex.status_code == 409
56
+
57
+    def is_over_limit(self, ex):
58
+        if not isinstance(ex, exceptions.NeutronClientException):
59
+            return False
60
+        return ex.status_code == 413

+ 0
- 0
gbpautomation/heat/engine/resources/__init__.py View File


+ 0
- 0
gbpautomation/heat/engine/resources/neutron/__init__.py View File


+ 151
- 0
gbpautomation/heat/engine/resources/neutron/gbpresource.py View File

@@ -0,0 +1,151 @@
1
+#
2
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
3
+#    not use this file except in compliance with the License. You may obtain
4
+#    a copy of the License at
5
+#
6
+#         http://www.apache.org/licenses/LICENSE-2.0
7
+#
8
+#    Unless required by applicable law or agreed to in writing, software
9
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11
+#    License for the specific language governing permissions and limitations
12
+#    under the License.
13
+
14
+from heat.common import exception
15
+from heat.engine import resource
16
+from heat.engine import scheduler
17
+from heat.openstack.common import log as logging
18
+
19
+LOG = logging.getLogger(__name__)
20
+
21
+
22
+class GBPResource(resource.Resource):
23
+
24
+    default_client_name = 'grouppolicy'
25
+
26
+    def grouppolicy(self):
27
+        return self.client('grouppolicy')
28
+
29
+    def validate(self):
30
+        '''
31
+        Validate any of the provided params
32
+        '''
33
+        res = super(GBPResource, self).validate()
34
+        if res:
35
+            return res
36
+        return self.validate_properties(self.properties)
37
+
38
+    @staticmethod
39
+    def validate_properties(properties):
40
+        '''
41
+        Validates to ensure nothing in value_specs overwrites
42
+        any key that exists in the schema.
43
+
44
+        Also ensures that shared and tenant_id is not specified
45
+        in value_specs.
46
+        '''
47
+        if 'value_specs' in properties.keys():
48
+            vs = properties.get('value_specs')
49
+            banned_keys = set(['shared', 'tenant_id']).union(
50
+                properties.keys())
51
+            for k in banned_keys.intersection(vs.keys()):
52
+                return '%s not allowed in value_specs' % k
53
+
54
+    @staticmethod
55
+    def _validate_depr_property_required(properties, prop_key, depr_prop_key):
56
+            prop_value = properties.get(prop_key)
57
+            depr_prop_value = properties.get(depr_prop_key)
58
+
59
+            if prop_value and depr_prop_value:
60
+                raise exception.ResourcePropertyConflict(prop_key,
61
+                                                         depr_prop_key)
62
+            if not prop_value and not depr_prop_value:
63
+                msg = _('Either %(prop_key)s or %(depr_prop_key)s'
64
+                        ' should be specified.'
65
+                        ) % {'prop_key': prop_key,
66
+                             'depr_prop_key': depr_prop_key}
67
+                raise exception.StackValidationFailed(message=msg)
68
+
69
+    @staticmethod
70
+    def prepare_properties(properties, name):
71
+        '''
72
+        Prepares the property values so that they can be passed directly to
73
+        the Neutron create call.
74
+
75
+        Removes None values and value_specs, merges value_specs with the main
76
+        values.
77
+        '''
78
+        props = dict((k, v) for k, v in properties.items()
79
+                     if v is not None and k != 'value_specs')
80
+
81
+        if 'name' in properties.keys():
82
+            props.setdefault('name', name)
83
+
84
+        if 'value_specs' in properties.keys():
85
+            props.update(properties.get('value_specs'))
86
+
87
+        return props
88
+
89
+    def prepare_update_properties(self, definition):
90
+        '''
91
+        Prepares the property values so that they can be passed directly to
92
+        the Neutron update call.
93
+
94
+        Removes any properties which are not update_allowed, then processes
95
+        as for prepare_properties.
96
+        '''
97
+        p = definition.properties(self.properties_schema, self.context)
98
+        update_props = dict((k, v) for k, v in p.items()
99
+                            if p.props.get(k).schema.update_allowed)
100
+
101
+        props = self.prepare_properties(
102
+            update_props,
103
+            self.physical_resource_name())
104
+        return props
105
+
106
+    @staticmethod
107
+    def is_built(attributes):
108
+        status = attributes['status']
109
+        if status == 'BUILD':
110
+            return False
111
+        if status in ('ACTIVE', 'DOWN'):
112
+            return True
113
+        elif status == 'ERROR':
114
+            raise resource.ResourceInError(
115
+                resource_status=status)
116
+        else:
117
+            raise resource.ResourceUnknownStatus(
118
+                resource_status=status,
119
+                result=_('Resource is not built'))
120
+
121
+    def _resolve_attribute(self, name):
122
+        try:
123
+            attributes = self._show_resource()
124
+        except Exception as ex:
125
+            self.client_plugin().ignore_not_found(ex)
126
+            return None
127
+        if name == 'show':
128
+            return attributes
129
+
130
+        return attributes[name]
131
+
132
+    def _confirm_delete(self):
133
+        while True:
134
+            try:
135
+                yield
136
+                self._show_resource()
137
+            except Exception as ex:
138
+                self.client_plugin().ignore_not_found(ex)
139
+                return
140
+
141
+    def FnGetRefId(self):
142
+        return unicode(self.resource_id)
143
+
144
+    def _delete_task(self):
145
+        delete_task = scheduler.TaskRunner(self._confirm_delete)
146
+        delete_task.start()
147
+        return delete_task
148
+
149
+    def check_delete_complete(self, delete_task):
150
+        # if the resource was already deleted, delete_task will be None
151
+        return delete_task is None or delete_task.step()

+ 357
- 0
gbpautomation/heat/engine/resources/neutron/grouppolicy.py View File

@@ -0,0 +1,357 @@
1
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
+
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    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, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+
16
+from gbpautomation.heat.engine.resources.neutron import gbpresource
17
+from neutronclient.common.exceptions import NeutronClientException
18
+
19
+from heat.engine import attributes
20
+from heat.engine import properties
21
+
22
+
23
+class Endpoint(gbpresource.GBPResource):
24
+
25
+    PROPERTIES = (
26
+        TENANT_ID, NAME, DESCRIPTION, ENDPOINT_GROUP_ID
27
+    ) = (
28
+        'tenant_id', 'name', 'description', 'endpoint_group_id'
29
+    )
30
+
31
+    ATTRIBUTES = (
32
+        NEUTRON_PORT_ID
33
+    ) = (
34
+        'neutron_port_id'
35
+    )
36
+
37
+    properties_schema = {
38
+        TENANT_ID: properties.Schema(
39
+            properties.Schema.STRING,
40
+            _('Tenant id of the endpoint.')
41
+        ),
42
+        NAME: properties.Schema(
43
+            properties.Schema.STRING,
44
+            _('Name of the endpoint.'),
45
+            update_allowed=True
46
+        ),
47
+        DESCRIPTION: properties.Schema(
48
+            properties.Schema.STRING,
49
+            _('Description of the endpoint.'),
50
+            update_allowed=True
51
+        ),
52
+        ENDPOINT_GROUP_ID: properties.Schema(
53
+            properties.Schema.STRING,
54
+            _('Endpoint group id of the endpoint.'),
55
+            required=True,
56
+            update_allowed=True
57
+        )
58
+    }
59
+
60
+    attributes_schema = {
61
+        NEUTRON_PORT_ID: attributes.Schema(
62
+            _("Neutron port id of this endpoint")
63
+        )
64
+    }
65
+
66
+    def _show_resource(self):
67
+        client = self.grouppolicy()
68
+        ep_id = self.resource_id
69
+        return client.show_endpoint(ep_id)['endpoint']
70
+
71
+    def handle_create(self):
72
+        client = self.grouppolicy()
73
+
74
+        props = {}
75
+        for key in self.properties:
76
+            if self.properties.get(key) is not None:
77
+                props[key] = self.properties.get(key)
78
+
79
+        ep = client.create_endpoint({'endpoint': props})['endpoint']
80
+
81
+        self.resource_id_set(ep['id'])
82
+
83
+    def _resolve_attribute(self, name):
84
+        client = self.grouppolicy()
85
+        ep_id = self.resource_id
86
+        if name == 'neutron_port_id':
87
+            return client.show_endpoint(ep_id)['endpoint']['neutron_port_id']
88
+        return super(Endpoint, self)._resolve_attribute(name)
89
+
90
+    def handle_delete(self):
91
+
92
+        client = self.grouppolicy()
93
+        ep_id = self.resource_id
94
+
95
+        try:
96
+            client.delete_endpoint(ep_id)
97
+        except NeutronClientException as ex:
98
+            self.client_plugin().ignore_not_found(ex)
99
+        else:
100
+            return self._delete_task()
101
+
102
+    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
103
+        if prop_diff:
104
+            self.grouppolicy().update_endpoint(
105
+                self.resource_id, {'endpoint': prop_diff})
106
+
107
+
108
+class EndpointGroup(gbpresource.GBPResource):
109
+
110
+    PROPERTIES = (
111
+        TENANT_ID, NAME, DESCRIPTION, L2_POLICY_ID,
112
+        PROVIDED_CONTRACTS, CONSUMED_CONTRACTS
113
+    ) = (
114
+        'tenant_id', 'name', 'description', 'l2_policy_id',
115
+        'provided_contracts', 'consumed_contracts'
116
+    )
117
+
118
+    properties_schema = {
119
+        TENANT_ID: properties.Schema(
120
+            properties.Schema.STRING,
121
+            _('Tenant id of the endpoint group.')
122
+        ),
123
+        NAME: properties.Schema(
124
+            properties.Schema.STRING,
125
+            _('Name of the endpoint group.'),
126
+            update_allowed=True
127
+        ),
128
+        DESCRIPTION: properties.Schema(
129
+            properties.Schema.STRING,
130
+            _('Description of the endpoint group.'),
131
+            update_allowed=True
132
+        ),
133
+        L2_POLICY_ID: properties.Schema(
134
+            properties.Schema.STRING,
135
+            _('L2 policy id of the endpoint group.'),
136
+            update_allowed=True
137
+        ),
138
+        PROVIDED_CONTRACTS: properties.Schema(
139
+            properties.Schema.LIST,
140
+            _('Provided contracts for the endpoint group.'),
141
+            update_allowed=True
142
+        ),
143
+        CONSUMED_CONTRACTS: properties.Schema(
144
+            properties.Schema.LIST,
145
+            _('Consumed contracts for the endpoint group.'),
146
+            update_allowed=True
147
+        )
148
+    }
149
+
150
+    def _show_resource(self):
151
+        client = self.grouppolicy()
152
+        epg_id = self.resource_id
153
+        return client.show_endpoint_group(epg_id)['endpoint_group']
154
+
155
+    def handle_create(self):
156
+        client = self.grouppolicy()
157
+
158
+        props = {}
159
+        for key in self.properties:
160
+            if self.properties.get(key) is not None:
161
+                props[key] = self.properties.get(key)
162
+
163
+        provided_contracts_list = {}
164
+        consumed_contracts_list = {}
165
+        props_provided_contracts = props.get('provided_contracts', [])
166
+        props_consumed_contracts = props.get('consumed_contracts', [])
167
+
168
+        for prop_prov_contract in props_provided_contracts:
169
+            contract_id = prop_prov_contract['contract_id']
170
+            contract_scope = prop_prov_contract['contract_scope']
171
+            provided_contracts_list.update({contract_id: contract_scope})
172
+
173
+        for prop_cons_contract in props_consumed_contracts:
174
+            contract_id = prop_cons_contract['contract_id']
175
+            contract_scope = prop_cons_contract['contract_scope']
176
+            consumed_contracts_list.update({contract_id: contract_scope})
177
+
178
+        if provided_contracts_list:
179
+            props['provided_contracts'] = provided_contracts_list
180
+        if consumed_contracts_list:
181
+            props['consumed_contracts'] = consumed_contracts_list
182
+
183
+        epg = client.create_endpoint_group(
184
+            {'endpoint_group': props})['endpoint_group']
185
+
186
+        self.resource_id_set(epg['id'])
187
+
188
+    def handle_delete(self):
189
+
190
+        client = self.grouppolicy()
191
+        epg_id = self.resource_id
192
+
193
+        try:
194
+            client.delete_endpoint_group(epg_id)
195
+        except NeutronClientException as ex:
196
+            self.client_plugin().ignore_not_found(ex)
197
+        else:
198
+            return self._delete_task()
199
+
200
+    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
201
+        if prop_diff:
202
+            self.grouppolicy().update_endpoint_group(
203
+                self.resource_id, {'endpoint_group': prop_diff})
204
+
205
+
206
+class L2Policy(gbpresource.GBPResource):
207
+
208
+    PROPERTIES = (
209
+        TENANT_ID, NAME, DESCRIPTION, L3_POLICY_ID
210
+    ) = (
211
+        'tenant_id', 'name', 'description', 'l3_policy_id'
212
+    )
213
+
214
+    properties_schema = {
215
+        TENANT_ID: properties.Schema(
216
+            properties.Schema.STRING,
217
+            _('Tenant id of the L2 policy.')
218
+        ),
219
+        NAME: properties.Schema(
220
+            properties.Schema.STRING,
221
+            _('Name of the L2 policy.'),
222
+            update_allowed=True
223
+        ),
224
+        DESCRIPTION: properties.Schema(
225
+            properties.Schema.STRING,
226
+            _('Description of the L2 policy.'),
227
+            update_allowed=True
228
+        ),
229
+        L3_POLICY_ID: properties.Schema(
230
+            properties.Schema.STRING,
231
+            _('L3 policy id associated with l2 policy.'),
232
+            required=True,
233
+            update_allowed=True
234
+        )
235
+    }
236
+
237
+    def _show_resource(self):
238
+        client = self.grouppolicy()
239
+        l2_policy_id = self.resource_id
240
+        return client.show_l2_policy(l2_policy_id)['l2_policy']
241
+
242
+    def handle_create(self):
243
+        client = self.grouppolicy()
244
+
245
+        props = {}
246
+        for key in self.properties:
247
+            if self.properties.get(key) is not None:
248
+                props[key] = self.properties.get(key)
249
+
250
+        l2_policy = client.create_l2_policy(
251
+            {'l2_policy': props})['l2_policy']
252
+
253
+        self.resource_id_set(l2_policy['id'])
254
+
255
+    def handle_delete(self):
256
+
257
+        client = self.grouppolicy()
258
+        l2_policy_id = self.resource_id
259
+
260
+        try:
261
+            client.delete_l2_policy(l2_policy_id)
262
+        except NeutronClientException as ex:
263
+            self.client_plugin().ignore_not_found(ex)
264
+        else:
265
+            return self._delete_task()
266
+
267
+    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
268
+        if prop_diff:
269
+            self.grouppolicy().update_l2_policy(
270
+                self.resource_id, {'l2_policy': prop_diff})
271
+
272
+
273
+class L3Policy(gbpresource.GBPResource):
274
+
275
+    PROPERTIES = (
276
+        TENANT_ID, NAME, DESCRIPTION, IP_VERSION, IP_POOL,
277
+        SUBNET_PREFIX_LENGTH
278
+    ) = (
279
+        'tenant_id', 'name', 'description', 'ip_version', 'ip_pool',
280
+        'subnet_prefix_length'
281
+    )
282
+
283
+    properties_schema = {
284
+        TENANT_ID: properties.Schema(
285
+            properties.Schema.STRING,
286
+            _('Tenant id of the L3 policy.')
287
+        ),
288
+        NAME: properties.Schema(
289
+            properties.Schema.STRING,
290
+            _('Name of the L3 policy.'),
291
+            update_allowed=True
292
+        ),
293
+        DESCRIPTION: properties.Schema(
294
+            properties.Schema.STRING,
295
+            _('Description of the L3 policy.'),
296
+            update_allowed=True
297
+        ),
298
+        IP_VERSION: properties.Schema(
299
+            properties.Schema.STRING,
300
+            _('IP version of the L3 policy.'),
301
+            update_allowed=False
302
+        ),
303
+        IP_POOL: properties.Schema(
304
+            properties.Schema.STRING,
305
+            _('IP pool of the L3 policy.'),
306
+            update_allowed=False
307
+        ),
308
+        SUBNET_PREFIX_LENGTH: properties.Schema(
309
+            properties.Schema.INTEGER,
310
+            _('Subnet prefix length of L3 policy.'),
311
+            update_allowed=True
312
+        )
313
+    }
314
+
315
+    def _show_resource(self):
316
+        client = self.grouppolicy()
317
+        l3_policy_id = self.resource_id
318
+        return client.show_l3_policy(l3_policy_id)['l3_policy']
319
+
320
+    def handle_create(self):
321
+        client = self.grouppolicy()
322
+
323
+        props = {}
324
+        for key in self.properties:
325
+            if self.properties.get(key) is not None:
326
+                props[key] = self.properties.get(key)
327
+
328
+        l3_policy = client.create_l3_policy(
329
+            {'l3_policy': props})['l3_policy']
330
+
331
+        self.resource_id_set(l3_policy['id'])
332
+
333
+    def handle_delete(self):
334
+
335
+        client = self.grouppolicy()
336
+        l3_policy_id = self.resource_id
337
+
338
+        try:
339
+            client.delete_l3_policy(l3_policy_id)
340
+        except NeutronClientException as ex:
341
+            self.client_plugin().ignore_not_found(ex)
342
+        else:
343
+            return self._delete_task()
344
+
345
+    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
346
+        if prop_diff:
347
+            self.grouppolicy().update_l3_policy(
348
+                self.resource_id, {'l3_policy': prop_diff})
349
+
350
+
351
+def resource_mapping():
352
+    return {
353
+        'OS::Neutron::Endpoint': Endpoint,
354
+        'OS::Neutron::EndpointGroup': EndpointGroup,
355
+        'OS::Neutron::L2Policy': L2Policy,
356
+        'OS::Neutron::L3Policy': L3Policy,
357
+    }

+ 611
- 0
gbpautomation/heat/tests/test_grouppolicy.py View File

@@ -0,0 +1,611 @@
1
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
2
+#    not use this file except in compliance with the License. You may obtain
3
+#    a copy of the License at
4
+#
5
+#         http://www.apache.org/licenses/LICENSE-2.0
6
+#
7
+#    Unless required by applicable law or agreed to in writing, software
8
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10
+#    License for the specific language governing permissions and limitations
11
+#    under the License.
12
+
13
+import copy
14
+
15
+from gbpautomation.heat.engine.resources.neutron import grouppolicy
16
+from gbpclient.v2_0 import client as gbpclient
17
+from heat.common import exception
18
+from heat.common import template_format
19
+from heat.tests.common import HeatTestCase
20
+
21
+from heat.engine import scheduler
22
+from heat.tests import utils
23
+
24
+
25
+endpoint_template = '''
26
+{
27
+  "AWSTemplateFormatVersion" : "2010-09-09",
28
+  "Description" : "Template to test neutron endpoint resource",
29
+  "Parameters" : {},
30
+  "Resources" : {
31
+    "endpoint": {
32
+      "Type": "OS::Neutron::Endpoint",
33
+      "Properties": {
34
+        "name": "test-endpoint",
35
+        "endpoint_group_id": "epg-id",
36
+        "description": "test endpoint resource"
37
+      }
38
+    }
39
+  }
40
+}
41
+'''
42
+
43
+endpoint_group_template = '''
44
+{
45
+  "AWSTemplateFormatVersion" : "2010-09-09",
46
+  "Description" : "Template to test neutron endpoint group resource",
47
+  "Parameters" : {},
48
+  "Resources" : {
49
+    "endpoint_group": {
50
+      "Type": "OS::Neutron::EndpointGroup",
51
+      "Properties": {
52
+        "name": "test-endpoint-group",
53
+        "description": "test endpoint group resource",
54
+        "l2_policy_id": "l2-policy-id",
55
+        "provided_contracts": [
56
+            {"contract_id": "contract1", "contract_scope": "scope1"},
57
+            {"contract_id": "contract2", "contract_scope": "scope2"}
58
+        ],
59
+        "consumed_contracts": [
60
+            {"contract_id": "contract3", "contract_scope": "scope3"},
61
+            {"contract_id": "contract4", "contract_scope": "scope4"}
62
+        ]
63
+      }
64
+    }
65
+  }
66
+}
67
+'''
68
+
69
+l2_policy_template = '''
70
+{
71
+  "AWSTemplateFormatVersion" : "2010-09-09",
72
+  "Description" : "Template to test neutron l2 policy",
73
+  "Parameters" : {},
74
+  "Resources" : {
75
+    "l2_policy": {
76
+      "Type": "OS::Neutron::L2Policy",
77
+      "Properties": {
78
+        "name": "test-l2-policy",
79
+        "description": "test L2 policy resource",
80
+        "l3_policy_id": "l3-policy-id"
81
+      }
82
+    }
83
+  }
84
+}
85
+'''
86
+
87
+l3_policy_template = '''
88
+{
89
+  "AWSTemplateFormatVersion" : "2010-09-09",
90
+  "Description" : "Template to test neutron l3 policy",
91
+  "Parameters" : {},
92
+  "Resources" : {
93
+    "l3_policy": {
94
+      "Type": "OS::Neutron::L3Policy",
95
+      "Properties": {
96
+        "name": "test-l3-policy",
97
+        "description": "test L3 policy resource",
98
+        "ip_version": "4",
99
+        "ip_pool": "10.20.20.0",
100
+        "subnet_prefix_length": 24
101
+      }
102
+    }
103
+  }
104
+}
105
+'''
106
+
107
+
108
+class EndpointTest(HeatTestCase):
109
+
110
+    def setUp(self):
111
+        super(EndpointTest, self).setUp()
112
+        self.m.StubOutWithMock(gbpclient.Client, 'create_endpoint')
113
+        self.m.StubOutWithMock(gbpclient.Client, 'delete_endpoint')
114
+        self.m.StubOutWithMock(gbpclient.Client, 'show_endpoint')
115
+        self.m.StubOutWithMock(gbpclient.Client, 'update_endpoint')
116
+        self.stub_keystoneclient()
117
+
118
+    def create_endpoint(self):
119
+        gbpclient.Client.create_endpoint({
120
+            'endpoint': {
121
+                'name': 'test-endpoint',
122
+                'endpoint_group_id': 'epg-id',
123
+                "description": "test endpoint resource"
124
+            }
125
+        }).AndReturn({'endpoint': {'id': '5678'}})
126
+
127
+        snippet = template_format.parse(endpoint_template)
128
+        stack = utils.parse_stack(snippet)
129
+        resource_defns = stack.t.resource_definitions(stack)
130
+        return grouppolicy.Endpoint(
131
+            'endpoint', resource_defns['endpoint'], stack)
132
+
133
+    def test_create(self):
134
+        rsrc = self.create_endpoint()
135
+        self.m.ReplayAll()
136
+        scheduler.TaskRunner(rsrc.create)()
137
+        self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
138
+        self.m.VerifyAll()
139
+
140
+    def test_create_failed(self):
141
+        gbpclient.Client.create_endpoint({
142
+            'endpoint': {
143
+                'name': 'test-endpoint',
144
+                'endpoint_group_id': 'epg-id',
145
+                "description": "test endpoint resource"
146
+            }
147
+        }).AndRaise(grouppolicy.NeutronClientException())
148
+        self.m.ReplayAll()
149
+
150
+        snippet = template_format.parse(endpoint_template)
151
+        stack = utils.parse_stack(snippet)
152
+        resource_defns = stack.t.resource_definitions(stack)
153
+        rsrc = grouppolicy.Endpoint(
154
+            'endpoint', resource_defns['endpoint'], stack)
155
+
156
+        error = self.assertRaises(exception.ResourceFailure,
157
+                                  scheduler.TaskRunner(rsrc.create))
158
+        self.assertEqual(
159
+            'NeutronClientException: An unknown exception occurred.',
160
+            str(error))
161
+        self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
162
+        self.m.VerifyAll()
163
+
164
+    def test_delete(self):
165
+        gbpclient.Client.delete_endpoint('5678')
166
+        gbpclient.Client.show_endpoint('5678').AndRaise(
167
+            grouppolicy.NeutronClientException(status_code=404))
168
+
169
+        rsrc = self.create_endpoint()
170
+        self.m.ReplayAll()
171
+        scheduler.TaskRunner(rsrc.create)()
172
+        scheduler.TaskRunner(rsrc.delete)()
173
+        self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
174
+        self.m.VerifyAll()
175
+
176
+    def test_delete_already_gone(self):
177
+        gbpclient.Client.delete_endpoint('5678').AndRaise(
178
+            grouppolicy.NeutronClientException(status_code=404))
179
+
180
+        rsrc = self.create_endpoint()
181
+        self.m.ReplayAll()
182
+        scheduler.TaskRunner(rsrc.create)()
183
+        scheduler.TaskRunner(rsrc.delete)()
184
+        self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
185
+        self.m.VerifyAll()
186
+
187
+    def test_delete_failed(self):
188
+        gbpclient.Client.delete_endpoint('5678').AndRaise(
189
+            grouppolicy.NeutronClientException(status_code=400))
190
+
191
+        rsrc = self.create_endpoint()
192
+        self.m.ReplayAll()
193
+        scheduler.TaskRunner(rsrc.create)()
194
+        error = self.assertRaises(exception.ResourceFailure,
195
+                                  scheduler.TaskRunner(rsrc.delete))
196
+        self.assertEqual(
197
+            'NeutronClientException: An unknown exception occurred.',
198
+            str(error))
199
+        self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state)
200
+        self.m.VerifyAll()
201
+
202
+    def test_attribute(self):
203
+        rsrc = self.create_endpoint()
204
+        gbpclient.Client.show_endpoint('5678').MultipleTimes(
205
+        ).AndReturn(
206
+            {'endpoint': {'neutron_port_id': '1234'}})
207
+        self.m.ReplayAll()
208
+        scheduler.TaskRunner(rsrc.create)()
209
+        self.assertEqual('1234', rsrc.FnGetAtt('neutron_port_id'))
210
+        self.m.VerifyAll()
211
+
212
+    def test_attribute_failed(self):
213
+        rsrc = self.create_endpoint()
214
+        self.m.ReplayAll()
215
+        scheduler.TaskRunner(rsrc.create)()
216
+        error = self.assertRaises(exception.InvalidTemplateAttribute,
217
+                                  rsrc.FnGetAtt, 'l2_policy_id')
218
+        self.assertEqual(
219
+            'The Referenced Attribute (endpoint l2_policy_id) is '
220
+            'incorrect.', str(error))
221
+        self.m.VerifyAll()
222
+
223
+    def test_update(self):
224
+        rsrc = self.create_endpoint()
225
+        gbpclient.Client.update_endpoint(
226
+            '5678', {'endpoint': {'endpoint_group_id': 'epg_id_update'}})
227
+        self.m.ReplayAll()
228
+        scheduler.TaskRunner(rsrc.create)()
229
+
230
+        update_template = copy.deepcopy(rsrc.t)
231
+        update_template['Properties']['endpoint_group_id'] = 'epg_id_update'
232
+        scheduler.TaskRunner(rsrc.update, update_template)()
233
+
234
+        self.m.VerifyAll()
235
+
236
+
237
+class EndpointGroupTest(HeatTestCase):
238
+
239
+    def setUp(self):
240
+        super(EndpointGroupTest, self).setUp()
241
+        self.m.StubOutWithMock(gbpclient.Client, 'create_endpoint_group')
242
+        self.m.StubOutWithMock(gbpclient.Client, 'delete_endpoint_group')
243
+        self.m.StubOutWithMock(gbpclient.Client, 'show_endpoint_group')
244
+        self.m.StubOutWithMock(gbpclient.Client, 'update_endpoint_group')
245
+        self.stub_keystoneclient()
246
+
247
+    def create_endpoint_group(self):
248
+        gbpclient.Client.create_endpoint_group({
249
+            "endpoint_group": {
250
+                "name": "test-endpoint-group",
251
+                "description": "test endpoint group resource",
252
+                "l2_policy_id": "l2-policy-id",
253
+                "provided_contracts": {
254
+                    "contract1": "scope1",
255
+                    "contract2": "scope2"
256
+                },
257
+                "consumed_contracts": {
258
+                    "contract3": "scope3",
259
+                    "contract4": "scope4"
260
+                }
261
+            }
262
+        }).AndReturn({'endpoint_group': {'id': '5678'}})
263
+
264
+        snippet = template_format.parse(endpoint_group_template)
265
+        stack = utils.parse_stack(snippet)
266
+        resource_defns = stack.t.resource_definitions(stack)
267
+        return grouppolicy.EndpointGroup(
268
+            'endpoint_group', resource_defns['endpoint_group'], stack)
269
+
270
+    def test_create(self):
271
+        rsrc = self.create_endpoint_group()
272
+        self.m.ReplayAll()
273
+        scheduler.TaskRunner(rsrc.create)()
274
+        self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
275
+        self.m.VerifyAll()
276
+
277
+    def test_create_failed(self):
278
+        gbpclient.Client.create_endpoint_group({
279
+            "endpoint_group": {
280
+                "name": "test-endpoint-group",
281
+                "description": "test endpoint group resource",
282
+                "l2_policy_id": "l2-policy-id",
283
+                "provided_contracts": {
284
+                    "contract1": "scope1",
285
+                    "contract2": "scope2"
286
+                },
287
+                "consumed_contracts": {
288
+                    "contract3": "scope3",
289
+                    "contract4": "scope4"
290
+                }
291
+            }
292
+        }).AndRaise(grouppolicy.NeutronClientException())
293
+        self.m.ReplayAll()
294
+
295
+        snippet = template_format.parse(endpoint_group_template)
296
+        stack = utils.parse_stack(snippet)
297
+        resource_defns = stack.t.resource_definitions(stack)
298
+        rsrc = grouppolicy.EndpointGroup(
299
+            'endpoint_group', resource_defns['endpoint_group'], stack)
300
+
301
+        error = self.assertRaises(exception.ResourceFailure,
302
+                                  scheduler.TaskRunner(rsrc.create))
303
+        self.assertEqual(
304
+            'NeutronClientException: An unknown exception occurred.',
305
+            str(error))
306
+        self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
307
+        self.m.VerifyAll()
308
+
309
+    def test_delete(self):
310
+        gbpclient.Client.delete_endpoint_group('5678')
311
+        gbpclient.Client.show_endpoint_group('5678').AndRaise(
312
+            grouppolicy.NeutronClientException(status_code=404))
313
+
314
+        rsrc = self.create_endpoint_group()
315
+        self.m.ReplayAll()
316
+        scheduler.TaskRunner(rsrc.create)()
317
+        scheduler.TaskRunner(rsrc.delete)()
318
+        self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
319
+        self.m.VerifyAll()
320
+
321
+    def test_delete_already_gone(self):
322
+        gbpclient.Client.delete_endpoint_group('5678').AndRaise(
323
+            grouppolicy.NeutronClientException(status_code=404))
324
+
325
+        rsrc = self.create_endpoint_group()
326
+        self.m.ReplayAll()
327
+        scheduler.TaskRunner(rsrc.create)()
328
+        scheduler.TaskRunner(rsrc.delete)()
329
+        self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
330
+        self.m.VerifyAll()
331
+
332
+    def test_delete_failed(self):
333
+        gbpclient.Client.delete_endpoint_group('5678').AndRaise(
334
+            grouppolicy.NeutronClientException(status_code=400))
335
+
336
+        rsrc = self.create_endpoint_group()
337
+        self.m.ReplayAll()
338
+        scheduler.TaskRunner(rsrc.create)()
339
+        error = self.assertRaises(exception.ResourceFailure,
340
+                                  scheduler.TaskRunner(rsrc.delete))
341
+        self.assertEqual(
342
+            'NeutronClientException: An unknown exception occurred.',
343
+            str(error))
344
+        self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state)
345
+        self.m.VerifyAll()
346
+
347
+    def test_attribute_failed(self):
348
+        rsrc = self.create_endpoint_group()
349
+        self.m.ReplayAll()
350
+        scheduler.TaskRunner(rsrc.create)()
351
+        error = self.assertRaises(exception.InvalidTemplateAttribute,
352
+                                  rsrc.FnGetAtt, 'l3_policy_id')
353
+        self.assertEqual(
354
+            'The Referenced Attribute (endpoint_group l3_policy_id) is '
355
+            'incorrect.', str(error))
356
+        self.m.VerifyAll()
357
+
358
+    def test_update(self):
359
+        rsrc = self.create_endpoint_group()
360
+        gbpclient.Client.update_endpoint_group(
361
+            '5678', {'endpoint_group': {'l2_policy_id': 'l2_id_update'}})
362
+        self.m.ReplayAll()
363
+        scheduler.TaskRunner(rsrc.create)()
364
+
365
+        update_template = copy.deepcopy(rsrc.t)
366
+        update_template['Properties']['l2_policy_id'] = 'l2_id_update'
367
+        scheduler.TaskRunner(rsrc.update, update_template)()
368
+
369
+        self.m.VerifyAll()
370
+
371
+
372
+class L2PolicyTest(HeatTestCase):
373
+
374
+    def setUp(self):
375
+        super(L2PolicyTest, self).setUp()
376
+        self.m.StubOutWithMock(gbpclient.Client, 'create_l2_policy')
377
+        self.m.StubOutWithMock(gbpclient.Client, 'delete_l2_policy')
378
+        self.m.StubOutWithMock(gbpclient.Client, 'show_l2_policy')
379
+        self.m.StubOutWithMock(gbpclient.Client, 'update_l2_policy')
380
+        self.stub_keystoneclient()
381
+
382
+    def create_l2_policy(self):
383
+        gbpclient.Client.create_l2_policy({
384
+            'l2_policy': {
385
+                "name": "test-l2-policy",
386
+                "description": "test L2 policy resource",
387
+                "l3_policy_id": "l3-policy-id"
388
+            }
389
+        }).AndReturn({'l2_policy': {'id': '5678'}})
390
+
391
+        snippet = template_format.parse(l2_policy_template)
392
+        stack = utils.parse_stack(snippet)
393
+        resource_defns = stack.t.resource_definitions(stack)
394
+        return grouppolicy.L2Policy(
395
+            'l2_policy', resource_defns['l2_policy'], stack)
396
+
397
+    def test_create(self):
398
+        rsrc = self.create_l2_policy()
399
+        self.m.ReplayAll()
400
+        scheduler.TaskRunner(rsrc.create)()
401
+        self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
402
+        self.m.VerifyAll()
403
+
404
+    def test_create_failed(self):
405
+        gbpclient.Client.create_l2_policy({
406
+            'l2_policy': {
407
+                "name": "test-l2-policy",
408
+                "description": "test L2 policy resource",
409
+                "l3_policy_id": "l3-policy-id"
410
+            }
411
+        }).AndRaise(grouppolicy.NeutronClientException())
412
+        self.m.ReplayAll()
413
+
414
+        snippet = template_format.parse(l2_policy_template)
415
+        stack = utils.parse_stack(snippet)
416
+        resource_defns = stack.t.resource_definitions(stack)
417
+        rsrc = grouppolicy.L2Policy(
418
+            'l2_policy', resource_defns['l2_policy'], stack)
419
+
420
+        error = self.assertRaises(exception.ResourceFailure,
421
+                                  scheduler.TaskRunner(rsrc.create))
422
+        self.assertEqual(
423
+            'NeutronClientException: An unknown exception occurred.',
424
+            str(error))
425
+        self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
426
+        self.m.VerifyAll()
427
+
428
+    def test_delete(self):
429
+        gbpclient.Client.delete_l2_policy('5678')
430
+        gbpclient.Client.show_l2_policy('5678').AndRaise(
431
+            grouppolicy.NeutronClientException(status_code=404))
432
+
433
+        rsrc = self.create_l2_policy()
434
+        self.m.ReplayAll()
435
+        scheduler.TaskRunner(rsrc.create)()
436
+        scheduler.TaskRunner(rsrc.delete)()
437
+        self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
438
+        self.m.VerifyAll()
439
+
440
+    def test_delete_already_gone(self):
441
+        gbpclient.Client.delete_l2_policy('5678').AndRaise(
442
+            grouppolicy.NeutronClientException(status_code=404))
443
+
444
+        rsrc = self.create_l2_policy()
445
+        self.m.ReplayAll()
446
+        scheduler.TaskRunner(rsrc.create)()
447
+        scheduler.TaskRunner(rsrc.delete)()
448
+        self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
449
+        self.m.VerifyAll()
450
+
451
+    def test_delete_failed(self):
452
+        gbpclient.Client.delete_l2_policy('5678').AndRaise(
453
+            grouppolicy.NeutronClientException(status_code=400))
454
+
455
+        rsrc = self.create_l2_policy()
456
+        self.m.ReplayAll()
457
+        scheduler.TaskRunner(rsrc.create)()
458
+        error = self.assertRaises(exception.ResourceFailure,
459
+                                  scheduler.TaskRunner(rsrc.delete))
460
+        self.assertEqual(
461
+            'NeutronClientException: An unknown exception occurred.',
462
+            str(error))
463
+        self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state)
464
+        self.m.VerifyAll()
465
+
466
+    def test_attribute_failed(self):
467
+        rsrc = self.create_l2_policy()
468
+        self.m.ReplayAll()
469
+        scheduler.TaskRunner(rsrc.create)()
470
+        error = self.assertRaises(exception.InvalidTemplateAttribute,
471
+                                  rsrc.FnGetAtt, 'endpoint_id')
472
+        self.assertEqual(
473
+            'The Referenced Attribute (l2_policy endpoint_id) is '
474
+            'incorrect.', str(error))
475
+        self.m.VerifyAll()
476
+
477
+    def test_update(self):
478
+        rsrc = self.create_l2_policy()
479
+        gbpclient.Client.update_l2_policy(
480
+            '5678', {'l2_policy': {'l3_policy_id': 'l3_id_update'}})
481
+        self.m.ReplayAll()
482
+        scheduler.TaskRunner(rsrc.create)()
483
+
484
+        update_template = copy.deepcopy(rsrc.t)
485
+        update_template['Properties']['l3_policy_id'] = 'l3_id_update'
486
+        scheduler.TaskRunner(rsrc.update, update_template)()
487
+
488
+        self.m.VerifyAll()
489
+
490
+
491
+class L3PolicyTest(HeatTestCase):
492
+
493
+    def setUp(self):
494
+        super(L3PolicyTest, self).setUp()
495
+        self.m.StubOutWithMock(gbpclient.Client, 'create_l3_policy')
496
+        self.m.StubOutWithMock(gbpclient.Client, 'delete_l3_policy')
497
+        self.m.StubOutWithMock(gbpclient.Client, 'show_l3_policy')
498
+        self.m.StubOutWithMock(gbpclient.Client, 'update_l3_policy')
499
+        self.stub_keystoneclient()
500
+
501
+    def create_l3_policy(self):
502
+        gbpclient.Client.create_l3_policy({
503
+            'l3_policy': {
504
+                "name": "test-l3-policy",
505
+                "description": "test L3 policy resource",
506
+                "ip_version": "4",
507
+                "ip_pool": "10.20.20.0",
508
+                "subnet_prefix_length": 24
509
+            }
510
+        }).AndReturn({'l3_policy': {'id': '5678'}})
511
+
512
+        snippet = template_format.parse(l3_policy_template)
513
+        stack = utils.parse_stack(snippet)
514
+        resource_defns = stack.t.resource_definitions(stack)
515
+        return grouppolicy.L3Policy(
516
+            'l3_policy', resource_defns['l3_policy'], stack)
517
+
518
+    def test_create(self):
519
+        rsrc = self.create_l3_policy()
520
+        self.m.ReplayAll()
521
+        scheduler.TaskRunner(rsrc.create)()
522
+        self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
523
+        self.m.VerifyAll()
524
+
525
+    def test_create_failed(self):
526
+        gbpclient.Client.create_l3_policy({
527
+            'l3_policy': {
528
+                "name": "test-l3-policy",
529
+                "description": "test L3 policy resource",
530
+                "ip_version": "4",
531
+                "ip_pool": "10.20.20.0",
532
+                "subnet_prefix_length": 24
533
+            }
534
+        }).AndRaise(grouppolicy.NeutronClientException())
535
+        self.m.ReplayAll()
536
+
537
+        snippet = template_format.parse(l3_policy_template)
538
+        stack = utils.parse_stack(snippet)
539
+        resource_defns = stack.t.resource_definitions(stack)
540
+        rsrc = grouppolicy.L3Policy(
541
+            'l3_policy', resource_defns['l3_policy'], stack)
542
+
543
+        error = self.assertRaises(exception.ResourceFailure,
544
+                                  scheduler.TaskRunner(rsrc.create))
545
+        self.assertEqual(
546
+            'NeutronClientException: An unknown exception occurred.',
547
+            str(error))
548
+        self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
549
+        self.m.VerifyAll()
550
+
551
+    def test_delete(self):
552
+        gbpclient.Client.delete_l3_policy('5678')
553
+        gbpclient.Client.show_l3_policy('5678').AndRaise(
554
+            grouppolicy.NeutronClientException(status_code=404))
555
+
556
+        rsrc = self.create_l3_policy()
557
+        self.m.ReplayAll()
558
+        scheduler.TaskRunner(rsrc.create)()
559
+        scheduler.TaskRunner(rsrc.delete)()
560
+        self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
561
+        self.m.VerifyAll()
562
+
563
+    def test_delete_already_gone(self):
564
+        gbpclient.Client.delete_l3_policy('5678').AndRaise(
565
+            grouppolicy.NeutronClientException(status_code=404))
566
+
567
+        rsrc = self.create_l3_policy()
568
+        self.m.ReplayAll()
569
+        scheduler.TaskRunner(rsrc.create)()
570
+        scheduler.TaskRunner(rsrc.delete)()
571
+        self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
572
+        self.m.VerifyAll()
573
+
574
+    def test_delete_failed(self):
575
+        gbpclient.Client.delete_l3_policy('5678').AndRaise(
576
+            grouppolicy.NeutronClientException(status_code=400))
577
+
578
+        rsrc = self.create_l3_policy()
579
+        self.m.ReplayAll()
580
+        scheduler.TaskRunner(rsrc.create)()
581
+        error = self.assertRaises(exception.ResourceFailure,
582
+                                  scheduler.TaskRunner(rsrc.delete))
583
+        self.assertEqual(
584
+            'NeutronClientException: An unknown exception occurred.',
585
+            str(error))
586
+        self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state)
587
+        self.m.VerifyAll()
588
+
589
+    def test_attribute_failed(self):
590
+        rsrc = self.create_l3_policy()
591
+        self.m.ReplayAll()
592
+        scheduler.TaskRunner(rsrc.create)()
593
+        error = self.assertRaises(exception.InvalidTemplateAttribute,
594
+                                  rsrc.FnGetAtt, 'subnet_id')
595
+        self.assertEqual(
596
+            'The Referenced Attribute (l3_policy subnet_id) is '
597
+            'incorrect.', str(error))
598
+        self.m.VerifyAll()
599
+
600
+    def test_update(self):
601
+        rsrc = self.create_l3_policy()
602
+        gbpclient.Client.update_l3_policy(
603
+            '5678', {'l3_policy': {'subnet_prefix_length': 28}})
604
+        self.m.ReplayAll()
605
+        scheduler.TaskRunner(rsrc.create)()
606
+
607
+        update_template = copy.deepcopy(rsrc.t)
608
+        update_template['Properties']['subnet_prefix_length'] = 28
609
+        scheduler.TaskRunner(rsrc.update, update_template)()
610
+
611
+        self.m.VerifyAll()

+ 5
- 1
setup.cfg View File

@@ -44,4 +44,8 @@ input_file = gbpautomation/locale/group-based-policy-automation.pot
44 44
 [extract_messages]
45 45
 keywords = _ gettext ngettext l_ lazy_gettext
46 46
 mapping_file = babel.cfg
47
-output_file = gbpautomation/locale/group-based-policy-automation.pot
47
+output_file = gbpautomation/locale/group-based-policy-automation.pot
48
+
49
+[entry_points]
50
+heat.clients =
51
+    grouppolicy = gbpautomation.heat.engine.clients.os.grouppolicy:GBPClientPlugin

+ 2
- 0
test-requirements.txt View File

@@ -3,12 +3,14 @@
3 3
 # process, which may cause wedges in the gate later.
4 4
 
5 5
 -e git://github.com/openstack/heat.git@stable/juno#egg=heat
6
+-e git://github.com/stackforge/python-group-based-policy-client.git@master#egg=gbpclient
6 7
 # Hacking already pins down pep8, pyflakes and flake8
7 8
 hacking>=0.8.0,<0.9
8 9
 coverage>=3.6
9 10
 discover
10 11
 lockfile>=0.8
11 12
 mock>=1.0
13
+python-subunit>=0.0.18
12 14
 mox>=0.5.3
13 15
 MySQL-python
14 16
 oslosphinx>=2.2.0  # Apache-2.0

Loading…
Cancel
Save