Browse Source

Merge "Implementation of GBP ServiceChain resources"

Jenkins 4 years ago
parent
commit
95fe4101ca

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

@@ -0,0 +1,166 @@
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 properties
20
+
21
+
22
+class ServiceChainNode(gbpresource.GBPResource):
23
+
24
+    PROPERTIES = (
25
+        TENANT_ID, NAME, DESCRIPTION, SERVICE_TYPE, CONFIG
26
+    ) = (
27
+        'tenant_id', 'name', 'description', 'service_type', 'config'
28
+    )
29
+
30
+    properties_schema = {
31
+        TENANT_ID: properties.Schema(
32
+            properties.Schema.STRING,
33
+            _('Tenant id of the service chain node.')
34
+        ),
35
+        NAME: properties.Schema(
36
+            properties.Schema.STRING,
37
+            _('Name of the service chain node.'),
38
+            update_allowed=True
39
+        ),
40
+        DESCRIPTION: properties.Schema(
41
+            properties.Schema.STRING,
42
+            _('Description of the service chain node.'),
43
+            update_allowed=True
44
+        ),
45
+        SERVICE_TYPE: properties.Schema(
46
+            properties.Schema.STRING,
47
+            _('Type of service in the service chain node.'),
48
+            required=True,
49
+            update_allowed=True
50
+        ),
51
+        CONFIG: properties.Schema(
52
+            properties.Schema.STRING,
53
+            _('Configuration of the service chain node.'),
54
+            required=True,
55
+            update_allowed=False
56
+        )
57
+    }
58
+
59
+    def _show_resource(self):
60
+        client = self.grouppolicy()
61
+        sc_node_id = self.resource_id
62
+        return client.show_servicechain_node(sc_node_id)['servicechain_node']
63
+
64
+    def handle_create(self):
65
+        client = self.grouppolicy()
66
+
67
+        props = {}
68
+        for key in self.properties:
69
+            if self.properties.get(key) is not None:
70
+                props[key] = self.properties.get(key)
71
+
72
+        sc_node = client.create_servicechain_node(
73
+            {'servicechain_node': props})['servicechain_node']
74
+
75
+        self.resource_id_set(sc_node['id'])
76
+
77
+    def handle_delete(self):
78
+
79
+        client = self.grouppolicy()
80
+        sc_node_id = self.resource_id
81
+
82
+        try:
83
+            client.delete_servicechain_node(sc_node_id)
84
+        except NeutronClientException as ex:
85
+            self.client_plugin().ignore_not_found(ex)
86
+        else:
87
+            return self._delete_task()
88
+
89
+    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
90
+        if prop_diff:
91
+            self.grouppolicy().update_servicechain_node(
92
+                self.resource_id, {'servicechain_node': prop_diff})
93
+
94
+
95
+class ServiceChainSpec(gbpresource.GBPResource):
96
+
97
+    PROPERTIES = (
98
+        TENANT_ID, NAME, DESCRIPTION, NODES
99
+    ) = (
100
+        'tenant_id', 'name', 'description', 'nodes'
101
+    )
102
+
103
+    properties_schema = {
104
+        TENANT_ID: properties.Schema(
105
+            properties.Schema.STRING,
106
+            _('Tenant id of the service chain spec.')
107
+        ),
108
+        NAME: properties.Schema(
109
+            properties.Schema.STRING,
110
+            _('Name of the service chain spec.'),
111
+            update_allowed=True
112
+        ),
113
+        DESCRIPTION: properties.Schema(
114
+            properties.Schema.STRING,
115
+            _('Description of the service chain spec.'),
116
+            update_allowed=True
117
+        ),
118
+        NODES: properties.Schema(
119
+            properties.Schema.LIST,
120
+            _('Nodes in the service chain spec.'),
121
+            required=True,
122
+            update_allowed=True
123
+        )
124
+    }
125
+
126
+    def _show_resource(self):
127
+        client = self.grouppolicy()
128
+        sc_spec_id = self.resource_id
129
+        return client.show_servicechain_spec(sc_spec_id)['servicechain_spec']
130
+
131
+    def handle_create(self):
132
+        client = self.grouppolicy()
133
+
134
+        props = {}
135
+        for key in self.properties:
136
+            if self.properties.get(key) is not None:
137
+                props[key] = self.properties.get(key)
138
+
139
+        sc_spec = client.create_servicechain_spec(
140
+            {'servicechain_spec': props})['servicechain_spec']
141
+
142
+        self.resource_id_set(sc_spec['id'])
143
+
144
+    def handle_delete(self):
145
+
146
+        client = self.grouppolicy()
147
+        sc_spec_id = self.resource_id
148
+
149
+        try:
150
+            client.delete_servicechain_spec(sc_spec_id)
151
+        except NeutronClientException as ex:
152
+            self.client_plugin().ignore_not_found(ex)
153
+        else:
154
+            return self._delete_task()
155
+
156
+    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
157
+        if prop_diff:
158
+            self.grouppolicy().update_servicechain_spec(
159
+                self.resource_id, {'servicechain_spec': prop_diff})
160
+
161
+
162
+def resource_mapping():
163
+    return {
164
+        'OS::Neutron::ServiceChainNode': ServiceChainNode,
165
+        'OS::Neutron::ServiceChainSpec': ServiceChainSpec,
166
+    }

+ 278
- 0
gbpautomation/heat/tests/test_servicechain.py View File

@@ -0,0 +1,278 @@
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 servicechain
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
+servicechain_node_template = '''
26
+{
27
+  "AWSTemplateFormatVersion" : "2010-09-09",
28
+  "Description" : "Template to test neutron service chain node",
29
+  "Parameters" : {},
30
+  "Resources" : {
31
+    "servicechain_node": {
32
+      "Type": "OS::Neutron::ServiceChainNode",
33
+      "Properties": {
34
+        "name": "test-sc-node",
35
+        "description": "test service chain node resource",
36
+        "service_type": "TAP",
37
+        "config": "{'name': 'sc_node_config'}"
38
+      }
39
+    }
40
+  }
41
+}
42
+'''
43
+
44
+servicechain_spec_template = '''
45
+{
46
+  "AWSTemplateFormatVersion" : "2010-09-09",
47
+  "Description" : "Template to test neutron service chain spec",
48
+  "Parameters" : {},
49
+  "Resources" : {
50
+    "servicechain_spec": {
51
+      "Type": "OS::Neutron::ServiceChainSpec",
52
+      "Properties": {
53
+        "name": "test-sc-spec",
54
+        "description": "test service chain spec resource",
55
+        "nodes": ["1234", "7890"]
56
+      }
57
+    }
58
+  }
59
+}
60
+'''
61
+
62
+
63
+class ServiceChainNodeTest(HeatTestCase):
64
+
65
+    def setUp(self):
66
+        super(ServiceChainNodeTest, self).setUp()
67
+        self.m.StubOutWithMock(gbpclient.Client, 'create_servicechain_node')
68
+        self.m.StubOutWithMock(gbpclient.Client, 'delete_servicechain_node')
69
+        self.m.StubOutWithMock(gbpclient.Client, 'show_servicechain_node')
70
+        self.m.StubOutWithMock(gbpclient.Client, 'update_servicechain_node')
71
+        self.stub_keystoneclient()
72
+
73
+    def create_servicechain_node(self):
74
+        gbpclient.Client.create_servicechain_node({
75
+            'servicechain_node': {
76
+                "name": "test-sc-node",
77
+                "description": "test service chain node resource",
78
+                "service_type": "TAP",
79
+                "config": "{'name': 'sc_node_config'}"
80
+            }
81
+        }).AndReturn({'servicechain_node': {'id': '5678'}})
82
+
83
+        snippet = template_format.parse(servicechain_node_template)
84
+        stack = utils.parse_stack(snippet)
85
+        resource_defns = stack.t.resource_definitions(stack)
86
+        return servicechain.ServiceChainNode(
87
+            'servicechain_node', resource_defns['servicechain_node'], stack)
88
+
89
+    def test_create(self):
90
+        rsrc = self.create_servicechain_node()
91
+        self.m.ReplayAll()
92
+        scheduler.TaskRunner(rsrc.create)()
93
+        self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
94
+        self.m.VerifyAll()
95
+
96
+    def test_create_failed(self):
97
+        gbpclient.Client.create_servicechain_node({
98
+            'servicechain_node': {
99
+                "name": "test-sc-node",
100
+                "description": "test service chain node resource",
101
+                "service_type": "TAP",
102
+                "config": "{'name': 'sc_node_config'}"
103
+            }
104
+        }).AndRaise(servicechain.NeutronClientException())
105
+        self.m.ReplayAll()
106
+
107
+        snippet = template_format.parse(servicechain_node_template)
108
+        stack = utils.parse_stack(snippet)
109
+        resource_defns = stack.t.resource_definitions(stack)
110
+        rsrc = servicechain.ServiceChainNode(
111
+            'servicechain_node', resource_defns['servicechain_node'], stack)
112
+
113
+        error = self.assertRaises(exception.ResourceFailure,
114
+                                  scheduler.TaskRunner(rsrc.create))
115
+        self.assertEqual(
116
+            'NeutronClientException: An unknown exception occurred.',
117
+            str(error))
118
+        self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
119
+        self.m.VerifyAll()
120
+
121
+    def test_delete(self):
122
+        gbpclient.Client.delete_servicechain_node('5678')
123
+        gbpclient.Client.show_servicechain_node('5678').AndRaise(
124
+            servicechain.NeutronClientException(status_code=404))
125
+
126
+        rsrc = self.create_servicechain_node()
127
+        self.m.ReplayAll()
128
+        scheduler.TaskRunner(rsrc.create)()
129
+        scheduler.TaskRunner(rsrc.delete)()
130
+        self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
131
+        self.m.VerifyAll()
132
+
133
+    def test_delete_already_gone(self):
134
+        gbpclient.Client.delete_servicechain_node('5678').AndRaise(
135
+            servicechain.NeutronClientException(status_code=404))
136
+
137
+        rsrc = self.create_servicechain_node()
138
+        self.m.ReplayAll()
139
+        scheduler.TaskRunner(rsrc.create)()
140
+        scheduler.TaskRunner(rsrc.delete)()
141
+        self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
142
+        self.m.VerifyAll()
143
+
144
+    def test_delete_failed(self):
145
+        gbpclient.Client.delete_servicechain_node('5678').AndRaise(
146
+            servicechain.NeutronClientException(status_code=400))
147
+
148
+        rsrc = self.create_servicechain_node()
149
+        self.m.ReplayAll()
150
+        scheduler.TaskRunner(rsrc.create)()
151
+        error = self.assertRaises(exception.ResourceFailure,
152
+                                  scheduler.TaskRunner(rsrc.delete))
153
+        self.assertEqual(
154
+            'NeutronClientException: An unknown exception occurred.',
155
+            str(error))
156
+        self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state)
157
+        self.m.VerifyAll()
158
+
159
+    def test_update(self):
160
+        rsrc = self.create_servicechain_node()
161
+        gbpclient.Client.update_servicechain_node(
162
+            '5678', {'servicechain_node': {'name': 'node_update'}})
163
+        self.m.ReplayAll()
164
+        scheduler.TaskRunner(rsrc.create)()
165
+
166
+        update_template = copy.deepcopy(rsrc.t)
167
+        update_template['Properties']['name'] = 'node_update'
168
+        scheduler.TaskRunner(rsrc.update, update_template)()
169
+
170
+        self.m.VerifyAll()
171
+
172
+
173
+class ServiceChainSpecTest(HeatTestCase):
174
+
175
+    def setUp(self):
176
+        super(ServiceChainSpecTest, self).setUp()
177
+        self.m.StubOutWithMock(gbpclient.Client, 'create_servicechain_spec')
178
+        self.m.StubOutWithMock(gbpclient.Client, 'delete_servicechain_spec')
179
+        self.m.StubOutWithMock(gbpclient.Client, 'show_servicechain_spec')
180
+        self.m.StubOutWithMock(gbpclient.Client, 'update_servicechain_spec')
181
+        self.stub_keystoneclient()
182
+
183
+    def create_servicechain_spec(self):
184
+        gbpclient.Client.create_servicechain_spec({
185
+            "servicechain_spec": {
186
+                "name": "test-sc-spec",
187
+                "description": "test service chain spec resource",
188
+                "nodes": ["1234", "7890"]
189
+            }
190
+        }).AndReturn({'servicechain_spec': {'id': '5678'}})
191
+
192
+        snippet = template_format.parse(servicechain_spec_template)
193
+        stack = utils.parse_stack(snippet)
194
+        resource_defns = stack.t.resource_definitions(stack)
195
+        return servicechain.ServiceChainSpec(
196
+            'servicechain_spec', resource_defns['servicechain_spec'], stack)
197
+
198
+    def test_create(self):
199
+        rsrc = self.create_servicechain_spec()
200
+        self.m.ReplayAll()
201
+        scheduler.TaskRunner(rsrc.create)()
202
+        self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
203
+        self.m.VerifyAll()
204
+
205
+    def test_create_failed(self):
206
+        gbpclient.Client.create_servicechain_spec({
207
+            'servicechain_spec': {
208
+                "name": "test-sc-spec",
209
+                "description": "test service chain spec resource",
210
+                "nodes": ["1234", "7890"]
211
+            }
212
+        }).AndRaise(servicechain.NeutronClientException())
213
+        self.m.ReplayAll()
214
+
215
+        snippet = template_format.parse(servicechain_spec_template)
216
+        stack = utils.parse_stack(snippet)
217
+        resource_defns = stack.t.resource_definitions(stack)
218
+        rsrc = servicechain.ServiceChainSpec(
219
+            'servicechain_spec', resource_defns['servicechain_spec'], stack)
220
+
221
+        error = self.assertRaises(exception.ResourceFailure,
222
+                                  scheduler.TaskRunner(rsrc.create))
223
+        self.assertEqual(
224
+            'NeutronClientException: An unknown exception occurred.',
225
+            str(error))
226
+        self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
227
+        self.m.VerifyAll()
228
+
229
+    def test_delete(self):
230
+        gbpclient.Client.delete_servicechain_spec('5678')
231
+        gbpclient.Client.show_servicechain_spec('5678').AndRaise(
232
+            servicechain.NeutronClientException(status_code=404))
233
+
234
+        rsrc = self.create_servicechain_spec()
235
+        self.m.ReplayAll()
236
+        scheduler.TaskRunner(rsrc.create)()
237
+        scheduler.TaskRunner(rsrc.delete)()
238
+        self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
239
+        self.m.VerifyAll()
240
+
241
+    def test_delete_already_gone(self):
242
+        gbpclient.Client.delete_servicechain_spec('5678').AndRaise(
243
+            servicechain.NeutronClientException(status_code=404))
244
+
245
+        rsrc = self.create_servicechain_spec()
246
+        self.m.ReplayAll()
247
+        scheduler.TaskRunner(rsrc.create)()
248
+        scheduler.TaskRunner(rsrc.delete)()
249
+        self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
250
+        self.m.VerifyAll()
251
+
252
+    def test_delete_failed(self):
253
+        gbpclient.Client.delete_servicechain_spec('5678').AndRaise(
254
+            servicechain.NeutronClientException(status_code=400))
255
+
256
+        rsrc = self.create_servicechain_spec()
257
+        self.m.ReplayAll()
258
+        scheduler.TaskRunner(rsrc.create)()
259
+        error = self.assertRaises(exception.ResourceFailure,
260
+                                  scheduler.TaskRunner(rsrc.delete))
261
+        self.assertEqual(
262
+            'NeutronClientException: An unknown exception occurred.',
263
+            str(error))
264
+        self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state)
265
+        self.m.VerifyAll()
266
+
267
+    def test_update(self):
268
+        rsrc = self.create_servicechain_spec()
269
+        gbpclient.Client.update_servicechain_spec(
270
+            '5678', {'servicechain_spec': {'name': 'spec_update'}})
271
+        self.m.ReplayAll()
272
+        scheduler.TaskRunner(rsrc.create)()
273
+
274
+        update_template = copy.deepcopy(rsrc.t)
275
+        update_template['Properties']['name'] = 'spec_update'
276
+        scheduler.TaskRunner(rsrc.update, update_template)()
277
+
278
+        self.m.VerifyAll()

Loading…
Cancel
Save