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

« back to all changes in this revision

Viewing changes to quantum/tests/unit/test_metadata_agent.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
#
 
3
# Copyright 2012 New Dream Network, LLC (DreamHost)
 
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: Mark McClain, DreamHost
 
18
 
 
19
import socket
 
20
 
 
21
import mock
 
22
import unittest2 as unittest
 
23
import webob
 
24
 
 
25
from quantum.agent.metadata import agent
 
26
 
 
27
 
 
28
class FakeConf(object):
 
29
    admin_user = 'quantum'
 
30
    admin_password = 'password'
 
31
    admin_tenant_name = 'tenant'
 
32
    auth_url = 'http://127.0.0.1'
 
33
    auth_strategy = 'keystone'
 
34
    auth_region = 'region'
 
35
    nova_metadata_ip = '9.9.9.9'
 
36
    nova_metadata_port = 8775
 
37
    metadata_proxy_shared_secret = 'secret'
 
38
 
 
39
 
 
40
class TestMetadataProxyHandler(unittest.TestCase):
 
41
    def setUp(self):
 
42
        self.qclient_p = mock.patch('quantumclient.v2_0.client.Client')
 
43
        self.qclient = self.qclient_p.start()
 
44
 
 
45
        self.log_p = mock.patch.object(agent, 'LOG')
 
46
        self.log = self.log_p.start()
 
47
 
 
48
        self.handler = agent.MetadataProxyHandler(FakeConf)
 
49
 
 
50
    def tearDown(self):
 
51
        self.log_p.stop()
 
52
        self.qclient_p.stop()
 
53
 
 
54
    def test_call(self):
 
55
        req = mock.Mock()
 
56
        with mock.patch.object(self.handler, '_get_instance_id') as get_id:
 
57
            get_id.return_value = 'id'
 
58
            with mock.patch.object(self.handler, '_proxy_request') as proxy:
 
59
                proxy.return_value = 'value'
 
60
 
 
61
                retval = self.handler(req)
 
62
                self.assertEqual(retval, 'value')
 
63
 
 
64
    def test_call_no_instance_match(self):
 
65
        req = mock.Mock()
 
66
        with mock.patch.object(self.handler, '_get_instance_id') as get_id:
 
67
            get_id.return_value = None
 
68
            retval = self.handler(req)
 
69
            self.assertIsInstance(retval, webob.exc.HTTPNotFound)
 
70
 
 
71
    def test_call_internal_server_error(self):
 
72
        req = mock.Mock()
 
73
        with mock.patch.object(self.handler, '_get_instance_id') as get_id:
 
74
            get_id.side_effect = Exception
 
75
            retval = self.handler(req)
 
76
            self.assertIsInstance(retval, webob.exc.HTTPInternalServerError)
 
77
            self.assertEqual(len(self.log.mock_calls), 2)
 
78
 
 
79
    def _get_instance_id_helper(self, headers, list_ports_retval,
 
80
                                networks=None, router_id=None):
 
81
        headers['X-Forwarded-For'] = '192.168.1.1'
 
82
        req = mock.Mock(headers=headers)
 
83
 
 
84
        def mock_list_ports(*args, **kwargs):
 
85
            return {'ports': list_ports_retval.pop(0)}
 
86
 
 
87
        self.qclient.return_value.list_ports.side_effect = mock_list_ports
 
88
        retval = self.handler._get_instance_id(req)
 
89
 
 
90
        expected = [
 
91
            mock.call(
 
92
                username=FakeConf.admin_user,
 
93
                tenant_name=FakeConf.admin_tenant_name,
 
94
                region_name=FakeConf.auth_region,
 
95
                auth_url=FakeConf.auth_url,
 
96
                password=FakeConf.admin_password,
 
97
                auth_strategy=FakeConf.auth_strategy)
 
98
        ]
 
99
 
 
100
        if router_id:
 
101
            expected.append(
 
102
                mock.call().list_ports(
 
103
                    device_id=router_id,
 
104
                    device_owner='network:router_interface'
 
105
                )
 
106
            )
 
107
 
 
108
        expected.append(
 
109
            mock.call().list_ports(
 
110
                network_id=networks or [],
 
111
                fixed_ips=['ip_address=192.168.1.1'])
 
112
        )
 
113
 
 
114
        self.qclient.assert_has_calls(expected)
 
115
 
 
116
        return retval
 
117
 
 
118
    def test_get_instance_id_router_id(self):
 
119
        router_id = 'the_id'
 
120
        headers = {
 
121
            'X-Quantum-Router-ID': router_id
 
122
        }
 
123
 
 
124
        networks = ['net1', 'net2']
 
125
        ports = [
 
126
            [{'network_id': 'net1'}, {'network_id': 'net2'}],
 
127
            [{'device_id': 'device_id'}]
 
128
        ]
 
129
 
 
130
        self.assertEqual(
 
131
            self._get_instance_id_helper(headers, ports, networks=networks,
 
132
                                         router_id=router_id),
 
133
            'device_id'
 
134
        )
 
135
 
 
136
    def test_get_instance_id_router_id_no_match(self):
 
137
        router_id = 'the_id'
 
138
        headers = {
 
139
            'X-Quantum-Router-ID': router_id
 
140
        }
 
141
 
 
142
        networks = ['net1', 'net2']
 
143
        ports = [
 
144
            [{'network_id': 'net1'}, {'network_id': 'net2'}],
 
145
            []
 
146
        ]
 
147
 
 
148
        self.assertIsNone(
 
149
            self._get_instance_id_helper(headers, ports, networks=networks,
 
150
                                         router_id=router_id),
 
151
        )
 
152
 
 
153
    def test_get_instance_id_network_id(self):
 
154
        network_id = 'the_id'
 
155
        headers = {
 
156
            'X-Quantum-Network-ID': network_id
 
157
        }
 
158
 
 
159
        ports = [
 
160
            [{'device_id': 'device_id'}]
 
161
        ]
 
162
 
 
163
        self.assertEqual(
 
164
            self._get_instance_id_helper(headers, ports, networks=['the_id']),
 
165
            'device_id'
 
166
        )
 
167
 
 
168
    def test_get_instance_id_network_id_no_match(self):
 
169
        network_id = 'the_id'
 
170
        headers = {
 
171
            'X-Quantum-Network-ID': network_id
 
172
        }
 
173
 
 
174
        ports = [[]]
 
175
 
 
176
        self.assertIsNone(
 
177
            self._get_instance_id_helper(headers, ports, networks=['the_id'])
 
178
        )
 
179
 
 
180
    def _proxy_request_test_helper(self, response_code):
 
181
        hdrs = {'X-Forwarded-For': '8.8.8.8'}
 
182
        req = mock.Mock(path_info='/the_path', query_string='', headers=hdrs)
 
183
        resp = mock.Mock(status=response_code)
 
184
        with mock.patch.object(self.handler, '_sign_instance_id') as sign:
 
185
            sign.return_value = 'signed'
 
186
            with mock.patch('httplib2.Http') as mock_http:
 
187
                mock_http.return_value.request.return_value = (resp, 'content')
 
188
 
 
189
                retval = self.handler._proxy_request('the_id', req)
 
190
                mock_http.assert_has_calls([
 
191
                    mock.call().request(
 
192
                        'http://9.9.9.9:8775/the_path',
 
193
                        headers={
 
194
                            'X-Forwarded-For': '8.8.8.8',
 
195
                            'X-Instance-ID-Signature': 'signed',
 
196
                            'X-Instance-ID': 'the_id'
 
197
                        }
 
198
                    )]
 
199
                )
 
200
 
 
201
                return retval
 
202
 
 
203
    def test_proxy_request_200(self):
 
204
        self.assertEqual('content', self._proxy_request_test_helper(200))
 
205
 
 
206
    def test_proxy_request_403(self):
 
207
        self.assertIsInstance(self._proxy_request_test_helper(403),
 
208
                              webob.exc.HTTPForbidden)
 
209
 
 
210
    def test_proxy_request_404(self):
 
211
        self.assertIsInstance(self._proxy_request_test_helper(404),
 
212
                              webob.exc.HTTPNotFound)
 
213
 
 
214
    def test_proxy_request_500(self):
 
215
        self.assertIsInstance(self._proxy_request_test_helper(500),
 
216
                              webob.exc.HTTPInternalServerError)
 
217
 
 
218
    def test_proxy_request_other_code(self):
 
219
        with self.assertRaises(Exception) as e:
 
220
            self._proxy_request_test_helper(302)
 
221
 
 
222
    def test_sign_instance_id(self):
 
223
        self.assertEqual(
 
224
            self.handler._sign_instance_id('foo'),
 
225
            '773ba44693c7553d6ee20f61ea5d2757a9a4f4a44d2841ae4e95b52e4cd62db4'
 
226
        )
 
227
 
 
228
 
 
229
class TestUnixDomainHttpProtocol(unittest.TestCase):
 
230
    def test_init_empty_client(self):
 
231
        u = agent.UnixDomainHttpProtocol(mock.Mock(), '', mock.Mock())
 
232
        self.assertEqual(u.client_address, ('<local>', 0))
 
233
 
 
234
    def test_init_with_client(self):
 
235
        u = agent.UnixDomainHttpProtocol(mock.Mock(), 'foo', mock.Mock())
 
236
        self.assertEqual(u.client_address, 'foo')
 
237
 
 
238
 
 
239
class TestUnixDomainWSGIServer(unittest.TestCase):
 
240
    def setUp(self):
 
241
        self.eventlet_p = mock.patch.object(agent, 'eventlet')
 
242
        self.eventlet = self.eventlet_p.start()
 
243
        self.server = agent.UnixDomainWSGIServer('test')
 
244
 
 
245
    def tearDown(self):
 
246
        self.eventlet_p.stop()
 
247
 
 
248
    def test_start(self):
 
249
        mock_app = mock.Mock()
 
250
        with mock.patch.object(self.server, 'pool') as pool:
 
251
            self.server.start(mock_app, '/the/path')
 
252
            self.eventlet.assert_has_calls([
 
253
                mock.call.listen(
 
254
                    '/the/path',
 
255
                    family=socket.AF_UNIX,
 
256
                    backlog=128
 
257
                )]
 
258
            )
 
259
            pool.spawn_n.assert_called_once_with(
 
260
                self.server._run,
 
261
                mock_app,
 
262
                self.eventlet.listen.return_value
 
263
            )
 
264
 
 
265
    def test_run(self):
 
266
        with mock.patch.object(agent, 'logging') as logging:
 
267
            self.server._run('app', 'sock')
 
268
 
 
269
            self.eventlet.wsgi.server.called_once_with(
 
270
                'sock',
 
271
                'app',
 
272
                self.server.pool,
 
273
                agent.UnixDomainHttpProtocol,
 
274
                mock.ANY
 
275
            )
 
276
            self.assertTrue(len(logging.mock_calls))
 
277
 
 
278
 
 
279
class TestUnixDomainMetadataProxy(unittest.TestCase):
 
280
    def setUp(self):
 
281
        self.cfg_p = mock.patch.object(agent, 'cfg')
 
282
        self.cfg = self.cfg_p.start()
 
283
        self.cfg.CONF.metadata_proxy_socket = '/the/path'
 
284
 
 
285
    def tearDown(self):
 
286
        self.cfg_p.stop()
 
287
 
 
288
    def test_init_doesnot_exists(self):
 
289
        with mock.patch('os.path.isdir') as isdir:
 
290
            with mock.patch('os.makedirs') as makedirs:
 
291
                isdir.return_value = False
 
292
                p = agent.UnixDomainMetadataProxy(mock.Mock())
 
293
 
 
294
                isdir.assert_called_once_with('/the')
 
295
                makedirs.assert_called_once_with('/the', 0755)
 
296
 
 
297
    def test_init_exists(self):
 
298
        with mock.patch('os.path.isdir') as isdir:
 
299
            with mock.patch('os.unlink') as unlink:
 
300
                isdir.return_value = True
 
301
                p = agent.UnixDomainMetadataProxy(mock.Mock())
 
302
 
 
303
                isdir.assert_called_once_with('/the')
 
304
                unlink.assert_called_once_with('/the/path')
 
305
 
 
306
    def test_init_exists_unlink_no_file(self):
 
307
        with mock.patch('os.path.isdir') as isdir:
 
308
            with mock.patch('os.unlink') as unlink:
 
309
                with mock.patch('os.path.exists') as exists:
 
310
                    isdir.return_value = True
 
311
                    exists.return_value = False
 
312
                    unlink.side_effect = OSError
 
313
 
 
314
                    p = agent.UnixDomainMetadataProxy(mock.Mock())
 
315
 
 
316
                    isdir.assert_called_once_with('/the')
 
317
                    unlink.assert_called_once_with('/the/path')
 
318
                    exists.assert_called_once_with('/the/path')
 
319
 
 
320
    def test_init_exists_unlink_fails_file_still_exists(self):
 
321
        with mock.patch('os.path.isdir') as isdir:
 
322
            with mock.patch('os.unlink') as unlink:
 
323
                with mock.patch('os.path.exists') as exists:
 
324
                    isdir.return_value = True
 
325
                    exists.return_value = True
 
326
                    unlink.side_effect = OSError
 
327
 
 
328
                    with self.assertRaises(OSError):
 
329
                        p = agent.UnixDomainMetadataProxy(mock.Mock())
 
330
 
 
331
                    isdir.assert_called_once_with('/the')
 
332
                    unlink.assert_called_once_with('/the/path')
 
333
                    exists.assert_called_once_with('/the/path')
 
334
 
 
335
    def test_run(self):
 
336
        with mock.patch.object(agent, 'MetadataProxyHandler') as handler:
 
337
            with mock.patch.object(agent, 'UnixDomainWSGIServer') as server:
 
338
                with mock.patch('os.path.isdir') as isdir:
 
339
                    with mock.patch('os.makedirs') as makedirs:
 
340
                        isdir.return_value = False
 
341
 
 
342
                        p = agent.UnixDomainMetadataProxy(self.cfg.CONF)
 
343
                        p.run()
 
344
 
 
345
                        isdir.assert_called_once_with('/the')
 
346
                        makedirs.assert_called_once_with('/the', 0755)
 
347
                        server.assert_has_calls([
 
348
                            mock.call('quantum-metadata-agent'),
 
349
                            mock.call().start(handler.return_value,
 
350
                                              '/the/path'),
 
351
                            mock.call().wait()]
 
352
                        )
 
353
 
 
354
    def test_main(self):
 
355
        with mock.patch.object(agent, 'UnixDomainMetadataProxy') as proxy:
 
356
            with mock.patch('eventlet.monkey_patch') as eventlet:
 
357
                with mock.patch.object(agent, 'config') as config:
 
358
                    with mock.patch.object(agent, 'cfg') as cfg:
 
359
                        agent.main()
 
360
 
 
361
                        self.assertTrue(eventlet.called)
 
362
                        self.assertTrue(config.setup_logging.called)
 
363
                        proxy.assert_has_calls([
 
364
                            mock.call(cfg.CONF),
 
365
                            mock.call().run()]
 
366
                        )