~ubuntu-branches/ubuntu/raring/quantum/raring-proposed

« back to all changes in this revision

Viewing changes to quantum/tests/unit/test_servicetype.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Chuck Short, Yolanda Robla, James Page, Maru Newby
  • Date: 2013-01-11 09:14:35 UTC
  • mfrom: (2.1.17)
  • Revision ID: package-import@ubuntu.com-20130111091435-vaup7dwmtmajy5oe
Tags: 2013.1~g2-0ubuntu1
[ Chuck Short ]
* New upstream version. 
* debian/patches/fix-quantum-configuration.patch: Refreshed.

[ Yolanda Robla ]
* debian/quantum-l3-agent.quantum-metadata-agent.upstart: Add
  upstart configuration for Metadata Agent.
* debian/quantum-l3-agent.install: Added quantum-ns-metadata-proxy,
  quantum-metadata-agent and metadata_agent.ini.
* debian/patches/fix-quantum-configuration.patch: Update rootwrap
  configuration in metadata_agent.ini file.
* debian/changelog: Updated package version
* d/p/fix-quantum-configuration.patch: refresh patches

[ James Page ]
* d/*.install: Install entry points from bin directory instead
  of easy-install ones generated during the package build process
  (LP: #1085038).
* d/control: Drop BD on python-dev-all; its not required.
* d/rules: Install multiple upstart configurations for quantum-l3-agent.
* d/control: Tidy package descriptions.
* d/*.postrm: Drop as debhelper will generate update-rc.d calls in
  maintainer scripts if required.
* d/quantum-common.postinst: Tweak permissions setting so that /etc/quantum
  is not owned/writable by the quantum user, ensure that /etc/quantum/rootwrap*
  is owned by root:root.
* d/*agent*.postinst: Dropped as permissions now correctly set in
  quantum-common.
* d/patches/fix-quantum-configuration.patch: Re-add dropped fixes rootwrap and
  sqlite defaults for all plugins.
* d/control: Added new BD on alembic (>= 0.4.1~), version python-mock >= 1.0b1.

[ Maru Newby ]
* debian/control: Remove unnecessary openvswitch-vswitch dependency
  from quantum-plugin-openvswitch (LP: #1076747).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 
2
# Copyright 2012 OpenStack LLC.
 
3
# All Rights Reserved.
 
4
#
 
5
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 
6
#    not use this file except in compliance with the License. You may obtain
 
7
#    a copy of the License at
 
8
#
 
9
#         http://www.apache.org/licenses/LICENSE-2.0
 
10
#
 
11
#    Unless required by applicable law or agreed to in writing, software
 
12
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
13
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
14
#    License for the specific language governing permissions and limitations
 
15
#    under the License.
 
16
#
 
17
#    @author: Salvatore Orlando, VMware
 
18
#
 
19
 
 
20
import contextlib
 
21
import logging
 
22
import unittest2 as unittest
 
23
 
 
24
import mock
 
25
import webob.exc as webexc
 
26
import webtest
 
27
 
 
28
from quantum.api import extensions
 
29
from quantum import context
 
30
from quantum.db import api as db_api
 
31
from quantum.db import models_v2
 
32
from quantum.db import servicetype_db
 
33
from quantum.extensions import servicetype
 
34
from quantum import manager
 
35
from quantum.openstack.common import cfg
 
36
from quantum.plugins.common import constants
 
37
from quantum.tests.unit import dummy_plugin as dp
 
38
from quantum.tests.unit import test_api_v2
 
39
from quantum.tests.unit import test_db_plugin
 
40
from quantum.tests.unit import test_extensions
 
41
 
 
42
 
 
43
LOG = logging.getLogger(__name__)
 
44
DEFAULT_SERVICE_DEFS = [{'service_class': constants.DUMMY,
 
45
                         'plugin': dp.DUMMY_PLUGIN_NAME}]
 
46
 
 
47
_uuid = test_api_v2._uuid
 
48
_get_path = test_api_v2._get_path
 
49
 
 
50
 
 
51
class TestServiceTypeExtensionManager(object):
 
52
    """ Mock extensions manager """
 
53
 
 
54
    def get_resources(self):
 
55
        return (servicetype.Servicetype.get_resources() +
 
56
                dp.Dummy.get_resources())
 
57
 
 
58
    def get_actions(self):
 
59
        return []
 
60
 
 
61
    def get_request_extensions(self):
 
62
        return []
 
63
 
 
64
 
 
65
class ServiceTypeTestCaseBase(unittest.TestCase):
 
66
 
 
67
    def setUp(self):
 
68
        # This is needed because otherwise a failure will occur due to
 
69
        # nonexisting core_plugin
 
70
        cfg.CONF.set_override('core_plugin', test_db_plugin.DB_PLUGIN_KLASS)
 
71
        cfg.CONF.set_override('service_plugins',
 
72
                              ["%s.%s" % (dp.__name__,
 
73
                                          dp.DummyServicePlugin.__name__)])
 
74
        # Make sure at each test a new instance of the plugin is returned
 
75
        manager.QuantumManager._instance = None
 
76
        # Ensure existing ExtensionManager is not used
 
77
        extensions.PluginAwareExtensionManager._instance = None
 
78
        ext_mgr = TestServiceTypeExtensionManager()
 
79
        self.ext_mdw = test_extensions.setup_extensions_middleware(ext_mgr)
 
80
        self.api = webtest.TestApp(self.ext_mdw)
 
81
        self.resource_name = servicetype.RESOURCE_NAME.replace('-', '_')
 
82
 
 
83
    def tearDown(self):
 
84
        self.api = None
 
85
        cfg.CONF.reset()
 
86
 
 
87
 
 
88
class ServiceTypeExtensionTestCase(ServiceTypeTestCaseBase):
 
89
 
 
90
    def setUp(self):
 
91
        self._patcher = mock.patch(
 
92
            "%s.%s" % (servicetype_db.__name__,
 
93
                       servicetype_db.ServiceTypeManager.__name__),
 
94
            autospec=True)
 
95
        self.mock_mgr = self._patcher.start()
 
96
        self.mock_mgr.get_instance.return_value = self.mock_mgr.return_value
 
97
        super(ServiceTypeExtensionTestCase, self).setUp()
 
98
 
 
99
    def tearDown(self):
 
100
        self._patcher.stop()
 
101
        super(ServiceTypeExtensionTestCase, self).tearDown()
 
102
 
 
103
    def _test_service_type_create(self, env=None,
 
104
                                  expected_status=webexc.HTTPCreated.code):
 
105
        tenant_id = 'fake'
 
106
        if env and 'quantum.context' in env:
 
107
            tenant_id = env['quantum.context'].tenant_id
 
108
 
 
109
        data = {self.resource_name:
 
110
                {'name': 'test',
 
111
                 'tenant_id': tenant_id,
 
112
                 'service_definitions':
 
113
                 [{'service_class': constants.DUMMY,
 
114
                   'plugin': dp.DUMMY_PLUGIN_NAME}]}}
 
115
        return_value = data[self.resource_name].copy()
 
116
        svc_type_id = _uuid()
 
117
        return_value['id'] = svc_type_id
 
118
 
 
119
        instance = self.mock_mgr.return_value
 
120
        instance.create_service_type.return_value = return_value
 
121
        expect_errors = expected_status >= webexc.HTTPBadRequest.code
 
122
        res = self.api.post_json(_get_path('service-types'), data,
 
123
                                 extra_environ=env,
 
124
                                 expect_errors=expect_errors)
 
125
        self.assertEqual(res.status_int, expected_status)
 
126
        if not expect_errors:
 
127
            instance.create_service_type.assert_called_with(mock.ANY,
 
128
                                                            service_type=data)
 
129
            self.assertTrue(self.resource_name in res.json)
 
130
            svc_type = res.json[self.resource_name]
 
131
            self.assertEqual(svc_type['id'], svc_type_id)
 
132
            # NOTE(salvatore-orlando): The following two checks are
 
133
            # probably not essential
 
134
            self.assertEqual(svc_type['service_definitions'],
 
135
                             data[self.resource_name]['service_definitions'])
 
136
 
 
137
    def _test_service_type_update(self, env=None,
 
138
                                  expected_status=webexc.HTTPOk.code):
 
139
        svc_type_name = 'updated'
 
140
        tenant_id = 'fake'
 
141
        if env and 'quantum.context' in env:
 
142
            tenant_id = env['quantum.context'].tenant_id
 
143
        data = {self.resource_name: {'name': svc_type_name,
 
144
                                     'tenant-id': tenant_id}}
 
145
        svc_type_id = _uuid()
 
146
        return_value = {'id': svc_type_id,
 
147
                        'name': svc_type_name}
 
148
 
 
149
        instance = self.mock_mgr.return_value
 
150
        expect_errors = expected_status >= webexc.HTTPBadRequest.code
 
151
        instance.update_service_type.return_value = return_value
 
152
        res = self.api.put_json(_get_path('service-types/%s' % svc_type_id),
 
153
                                data)
 
154
        if not expect_errors:
 
155
            instance.update_service_type.assert_called_with(mock.ANY,
 
156
                                                            svc_type_id,
 
157
                                                            service_type=data)
 
158
            self.assertEqual(res.status_int, webexc.HTTPOk.code)
 
159
            self.assertTrue(self.resource_name in res.json)
 
160
            svc_type = res.json[self.resource_name]
 
161
            self.assertEqual(svc_type['id'], svc_type_id)
 
162
            self.assertEqual(svc_type['name'],
 
163
                             data[self.resource_name]['name'])
 
164
 
 
165
    def test_service_type_create(self):
 
166
        self._test_service_type_create()
 
167
 
 
168
    def test_service_type_update(self):
 
169
        self._test_service_type_update()
 
170
 
 
171
    def test_service_type_delete(self):
 
172
        svctype_id = _uuid()
 
173
        instance = self.mock_mgr.return_value
 
174
        res = self.api.delete(_get_path('service-types/%s' % svctype_id))
 
175
        instance.delete_service_type.assert_called_with(mock.ANY,
 
176
                                                        svctype_id)
 
177
        self.assertEqual(res.status_int, webexc.HTTPNoContent.code)
 
178
 
 
179
    def test_service_type_get(self):
 
180
        svctype_id = _uuid()
 
181
        return_value = {self.resource_name: {'name': 'test',
 
182
                                             'service_definitions': [],
 
183
                                             'id': svctype_id}}
 
184
 
 
185
        instance = self.mock_mgr.return_value
 
186
        instance.get_service_type.return_value = return_value
 
187
 
 
188
        res = self.api.get(_get_path('service-types/%s' % svctype_id))
 
189
 
 
190
        instance.get_service_type.assert_called_with(mock.ANY,
 
191
                                                     svctype_id,
 
192
                                                     fields=mock.ANY)
 
193
        self.assertEqual(res.status_int, webexc.HTTPOk.code)
 
194
 
 
195
    def test_service_type_list(self):
 
196
        svctype_id = _uuid()
 
197
        return_value = [{self.resource_name: {'name': 'test',
 
198
                                              'service_definitions': [],
 
199
                                              'id': svctype_id}}]
 
200
 
 
201
        instance = self.mock_mgr.return_value
 
202
        instance.get_service_types.return_value = return_value
 
203
 
 
204
        res = self.api.get(_get_path('service-types'))
 
205
 
 
206
        instance.get_service_types.assert_called_with(mock.ANY,
 
207
                                                      fields=mock.ANY,
 
208
                                                      filters=mock.ANY)
 
209
        self.assertEqual(res.status_int, webexc.HTTPOk.code)
 
210
 
 
211
    def test_create_service_type_nonadminctx_returns_403(self):
 
212
        tenant_id = _uuid()
 
213
        env = {'quantum.context': context.Context('', tenant_id,
 
214
                                                  is_admin=False)}
 
215
        self._test_service_type_create(
 
216
            env=env, expected_status=webexc.HTTPForbidden.code)
 
217
 
 
218
    def test_create_service_type_adminctx_returns_200(self):
 
219
        env = {'quantum.context': context.Context('', '', is_admin=True)}
 
220
        self._test_service_type_create(env=env)
 
221
 
 
222
    def test_update_service_type_nonadminctx_returns_403(self):
 
223
        tenant_id = _uuid()
 
224
        env = {'quantum.context': context.Context('', tenant_id,
 
225
                                                  is_admin=False)}
 
226
        self._test_service_type_update(
 
227
            env=env, expected_status=webexc.HTTPForbidden.code)
 
228
 
 
229
    def test_update_service_type_adminctx_returns_200(self):
 
230
        env = {'quantum.context': context.Context('', '', is_admin=True)}
 
231
        self._test_service_type_update(env=env)
 
232
 
 
233
 
 
234
class ServiceTypeManagerTestCase(ServiceTypeTestCaseBase):
 
235
 
 
236
    def setUp(self):
 
237
        db_api._ENGINE = None
 
238
        db_api._MAKER = None
 
239
        # Blank out service type manager instance
 
240
        servicetype_db.ServiceTypeManager._instance = None
 
241
        plugin_name = "%s.%s" % (dp.__name__, dp.DummyServicePlugin.__name__)
 
242
        cfg.CONF.set_override('service_definition', ['dummy:%s' % plugin_name],
 
243
                              group='DEFAULT_SERVICETYPE')
 
244
        super(ServiceTypeManagerTestCase, self).setUp()
 
245
 
 
246
    def tearDown(self):
 
247
        super(ServiceTypeManagerTestCase, self).tearDown()
 
248
        db_api.clear_db()
 
249
 
 
250
    @contextlib.contextmanager
 
251
    def service_type(self, name='svc_type',
 
252
                     default=True,
 
253
                     service_defs=None,
 
254
                     do_delete=True):
 
255
        if not service_defs:
 
256
            service_defs = [{'service_class': constants.DUMMY,
 
257
                             'plugin': dp.DUMMY_PLUGIN_NAME}]
 
258
        res = self._create_service_type(name, service_defs)
 
259
        svc_type = res.json
 
260
        if res.status_int >= 400:
 
261
            raise webexc.HTTPClientError(code=res.status_int)
 
262
        yield svc_type
 
263
 
 
264
        if do_delete:
 
265
            # The do_delete parameter allows you to control whether the
 
266
            # created network is immediately deleted again. Therefore, this
 
267
            # function is also usable in tests, which require the creation
 
268
            # of many networks.
 
269
            self._delete_service_type(svc_type[self.resource_name]['id'])
 
270
 
 
271
    def _list_service_types(self):
 
272
        return self.api.get(_get_path('service-types'))
 
273
 
 
274
    def _show_service_type(self, svctype_id, expect_errors=False):
 
275
        return self.api.get(_get_path('service-types/%s' % str(svctype_id)),
 
276
                            expect_errors=expect_errors)
 
277
 
 
278
    def _create_service_type(self, name, service_defs,
 
279
                             default=None, expect_errors=False):
 
280
        data = {self.resource_name:
 
281
                {'name': name,
 
282
                 'service_definitions': service_defs}
 
283
                }
 
284
        if default:
 
285
            data[self.resource_name]['default'] = default
 
286
        if not 'tenant_id' in data[self.resource_name]:
 
287
            data[self.resource_name]['tenant_id'] = 'fake'
 
288
        return self.api.post_json(_get_path('service-types'), data,
 
289
                                  expect_errors=expect_errors)
 
290
 
 
291
    def _create_dummy(self, dummyname='dummyobject'):
 
292
        data = {'dummy': {'name': dummyname,
 
293
                          'tenant_id': 'fake'}}
 
294
        dummy_res = self.api.post_json(_get_path('dummys'), data)
 
295
        return dummy_res.json['dummy']
 
296
 
 
297
    def _update_service_type(self, svc_type_id, name, service_defs,
 
298
                             default=None, expect_errors=False):
 
299
        data = {self.resource_name:
 
300
                {'name': name}}
 
301
        if service_defs is not None:
 
302
            data[self.resource_name]['service_definitions'] = service_defs
 
303
        # set this attribute only if True
 
304
        if default:
 
305
            data[self.resource_name]['default'] = default
 
306
        return self.api.put_json(
 
307
            _get_path('service-types/%s' % str(svc_type_id)), data,
 
308
            expect_errors=expect_errors)
 
309
 
 
310
    def _delete_service_type(self, svctype_id, expect_errors=False):
 
311
        return self.api.delete(_get_path('service-types/%s' % str(svctype_id)),
 
312
                               expect_errors=expect_errors)
 
313
 
 
314
    def _validate_service_type(self, res, name, service_defs,
 
315
                               svc_type_id=None):
 
316
        self.assertTrue(self.resource_name in res.json)
 
317
        svc_type = res.json[self.resource_name]
 
318
        if svc_type_id:
 
319
            self.assertEqual(svc_type['id'], svc_type_id)
 
320
        if name:
 
321
            self.assertEqual(svc_type['name'], name)
 
322
        if service_defs:
 
323
            # unspecified drivers will value None in response
 
324
            for svc_def in service_defs:
 
325
                svc_def['driver'] = svc_def.get('driver')
 
326
            self.assertEqual(svc_type['service_definitions'],
 
327
                             service_defs)
 
328
        self.assertEqual(svc_type['default'], False)
 
329
 
 
330
    def _test_service_type_create(self, name='test',
 
331
                                  service_defs=DEFAULT_SERVICE_DEFS,
 
332
                                  default=None,
 
333
                                  expected_status=webexc.HTTPCreated.code):
 
334
        expect_errors = expected_status >= webexc.HTTPBadRequest.code
 
335
        res = self._create_service_type(name, service_defs,
 
336
                                        default, expect_errors)
 
337
        self.assertEqual(res.status_int, expected_status)
 
338
        if not expect_errors:
 
339
            self.assertEqual(res.status_int, webexc.HTTPCreated.code)
 
340
            self._validate_service_type(res, name, service_defs)
 
341
 
 
342
    def _test_service_type_update(self, svc_type_id, name='test-updated',
 
343
                                  default=None, service_defs=None,
 
344
                                  expected_status=webexc.HTTPOk.code):
 
345
        expect_errors = expected_status >= webexc.HTTPBadRequest.code
 
346
        res = self._update_service_type(svc_type_id, name, service_defs,
 
347
                                        default, expect_errors)
 
348
        if not expect_errors:
 
349
            self.assertEqual(res.status_int, webexc.HTTPOk.code)
 
350
            self._validate_service_type(res, name, service_defs, svc_type_id)
 
351
 
 
352
    def test_service_type_create(self):
 
353
        self._test_service_type_create()
 
354
 
 
355
    def test_create_service_type_default_returns_400(self):
 
356
        self._test_service_type_create(
 
357
            default=True, expected_status=webexc.HTTPBadRequest.code)
 
358
 
 
359
    def test_create_service_type_no_svcdef_returns_400(self):
 
360
        self._test_service_type_create(
 
361
            service_defs=None,
 
362
            expected_status=webexc.HTTPBadRequest.code)
 
363
 
 
364
    def test_service_type_update_name(self):
 
365
        with self.service_type() as svc_type:
 
366
            self._test_service_type_update(svc_type[self.resource_name]['id'])
 
367
 
 
368
    def test_service_type_update_set_default_returns_400(self):
 
369
        with self.service_type() as svc_type:
 
370
            self._test_service_type_update(
 
371
                svc_type[self.resource_name]['id'], default=True,
 
372
                expected_status=webexc.HTTPBadRequest.code)
 
373
 
 
374
    def test_service_type_update_clear_svc_defs_returns_400(self):
 
375
        with self.service_type() as svc_type:
 
376
            self._test_service_type_update(
 
377
                svc_type[self.resource_name]['id'], service_defs=[],
 
378
                expected_status=webexc.HTTPBadRequest.code)
 
379
 
 
380
    def test_service_type_update_svc_defs(self):
 
381
        with self.service_type() as svc_type:
 
382
            svc_defs = [{'service': constants.DUMMY,
 
383
                         'plugin': 'foobar'}]
 
384
            self._test_service_type_update(
 
385
                svc_type[self.resource_name]['id'], service_defs=svc_defs,
 
386
                expected_status=webexc.HTTPBadRequest.code)
 
387
 
 
388
    def test_list_service_types(self):
 
389
        with contextlib.nested(self.service_type('st1'),
 
390
                               self.service_type('st2')):
 
391
            res = self._list_service_types()
 
392
            self.assertEqual(res.status_int, webexc.HTTPOk.code)
 
393
            data = res.json
 
394
            self.assertTrue('service_types' in data)
 
395
            # it must be 3 because we have the default service type too!
 
396
            self.assertEquals(len(data['service_types']), 3)
 
397
 
 
398
    def test_get_default_service_type(self):
 
399
        res = self._list_service_types()
 
400
        self.assertEqual(res.status_int, webexc.HTTPOk.code)
 
401
        data = res.json
 
402
        self.assertTrue('service_types' in data)
 
403
        self.assertEquals(len(data['service_types']), 1)
 
404
        def_svc_type = data['service_types'][0]
 
405
        self.assertEqual(def_svc_type['default'], True)
 
406
 
 
407
    def test_get_service_type(self):
 
408
        with self.service_type() as svc_type:
 
409
            svc_type_data = svc_type[self.resource_name]
 
410
            res = self._show_service_type(svc_type_data['id'])
 
411
            self.assertEqual(res.status_int, webexc.HTTPOk.code)
 
412
            self._validate_service_type(res, svc_type_data['name'],
 
413
                                        svc_type_data['service_definitions'],
 
414
                                        svc_type_data['id'])
 
415
 
 
416
    def test_delete_service_type_in_use_returns_409(self):
 
417
        with self.service_type() as svc_type:
 
418
            svc_type_data = svc_type[self.resource_name]
 
419
            mgr = servicetype_db.ServiceTypeManager.get_instance()
 
420
            ctx = context.Context('', '', is_admin=True)
 
421
            mgr.increase_service_type_refcount(ctx, svc_type_data['id'])
 
422
            res = self._delete_service_type(svc_type_data['id'], True)
 
423
            self.assertEquals(res.status_int, webexc.HTTPConflict.code)
 
424
            mgr.decrease_service_type_refcount(ctx, svc_type_data['id'])
 
425
 
 
426
    def test_create_dummy_increases_service_type_refcount(self):
 
427
        dummy = self._create_dummy()
 
428
        svc_type_res = self._show_service_type(dummy['service_type'])
 
429
        svc_type = svc_type_res.json[self.resource_name]
 
430
        self.assertEquals(svc_type['num_instances'], 1)
 
431
 
 
432
    def test_delete_dummy_decreases_service_type_refcount(self):
 
433
        dummy = self._create_dummy()
 
434
        svc_type_res = self._show_service_type(dummy['service_type'])
 
435
        svc_type = svc_type_res.json[self.resource_name]
 
436
        self.assertEquals(svc_type['num_instances'], 1)
 
437
        self.api.delete(_get_path('dummys/%s' % str(dummy['id'])))
 
438
        svc_type_res = self._show_service_type(dummy['service_type'])
 
439
        svc_type = svc_type_res.json[self.resource_name]
 
440
        self.assertEquals(svc_type['num_instances'], 0)