~niedbalski/charms/trusty/rabbitmq-server/fix-lp-1489053

« back to all changes in this revision

Viewing changes to tests/basic_deployment.py

  • Committer: Liam Young
  • Date: 2015-09-09 13:12:47 UTC
  • mfrom: (110.1.4 rabbitmq-server)
  • Revision ID: liam.young@canonical.com-20150909131247-16hxw74o91c57kpg
[1chb1n, r=gnuoy] Refactor amulet tests, deprecate old tests

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
"""
 
3
Basic 3-node rabbitmq-server native cluster + nrpe functional tests
 
4
 
 
5
Cinder is present to exercise and inspect amqp relation functionality.
 
6
 
 
7
Each individual test is idempotent, in that it creates/deletes
 
8
a rmq test user, enables or disables ssl as needed.
 
9
 
 
10
Test order is not required, however tests are numbered to keep
 
11
relevant tests grouped together in run order.
 
12
"""
 
13
 
 
14
import amulet
 
15
import time
 
16
 
 
17
from charmhelpers.contrib.openstack.amulet.deployment import (
 
18
    OpenStackAmuletDeployment
 
19
)
 
20
 
 
21
from charmhelpers.contrib.openstack.amulet.utils import (
 
22
    OpenStackAmuletUtils,
 
23
    DEBUG,
 
24
    # ERROR
 
25
)
 
26
 
 
27
# Use DEBUG to turn on debug logging
 
28
u = OpenStackAmuletUtils(DEBUG)
 
29
 
 
30
 
 
31
class RmqBasicDeployment(OpenStackAmuletDeployment):
 
32
    """Amulet tests on a basic rabbitmq cluster deployment. Verify
 
33
       relations, service status, users and endpoint service catalog."""
 
34
 
 
35
    def __init__(self, series=None, openstack=None, source=None, stable=False):
 
36
        """Deploy the entire test environment."""
 
37
        super(RmqBasicDeployment, self).__init__(series, openstack, source,
 
38
                                                 stable)
 
39
        self._add_services()
 
40
        self._add_relations()
 
41
        self._configure_services()
 
42
        self._deploy()
 
43
        self._initialize_tests()
 
44
 
 
45
    def _add_services(self):
 
46
        """Add services
 
47
 
 
48
           Add the services that we're testing, where rmq is local,
 
49
           and the rest of the service are from lp branches that are
 
50
           compatible with the local charm (e.g. stable or next).
 
51
           """
 
52
        this_service = {
 
53
            'name': 'rabbitmq-server',
 
54
            'units': 3
 
55
        }
 
56
        other_services = [{'name': 'cinder'},
 
57
                          {'name': 'nrpe'}]
 
58
 
 
59
        super(RmqBasicDeployment, self)._add_services(this_service,
 
60
                                                      other_services)
 
61
 
 
62
    def _add_relations(self):
 
63
        """Add relations for the services."""
 
64
        relations = {'cinder:amqp': 'rabbitmq-server:amqp',
 
65
                     'nrpe:nrpe-external-master':
 
66
                     'rabbitmq-server:nrpe-external-master'}
 
67
 
 
68
        super(RmqBasicDeployment, self)._add_relations(relations)
 
69
 
 
70
    def _configure_services(self):
 
71
        """Configure all of the services."""
 
72
        rmq_config = {
 
73
            'min-cluster-size': '3',
 
74
            'max-cluster-tries': '6',
 
75
            'ssl': 'off',
 
76
            'management_plugin': 'False',
 
77
            'stats_cron_schedule': '*/1 * * * *'
 
78
        }
 
79
        cinder_config = {}
 
80
        configs = {'rabbitmq-server': rmq_config,
 
81
                   'cinder': cinder_config}
 
82
        super(RmqBasicDeployment, self)._configure_services(configs)
 
83
 
 
84
    def _initialize_tests(self):
 
85
        """Perform final initialization before tests get run."""
 
86
        # Access the sentries for inspecting service units
 
87
        self.rmq0_sentry = self.d.sentry.unit['rabbitmq-server/0']
 
88
        self.rmq1_sentry = self.d.sentry.unit['rabbitmq-server/1']
 
89
        self.rmq2_sentry = self.d.sentry.unit['rabbitmq-server/2']
 
90
        self.cinder_sentry = self.d.sentry.unit['cinder/0']
 
91
        self.nrpe_sentry = self.d.sentry.unit['nrpe/0']
 
92
        u.log.debug('openstack release val: {}'.format(
 
93
            self._get_openstack_release()))
 
94
        u.log.debug('openstack release str: {}'.format(
 
95
            self._get_openstack_release_string()))
 
96
 
 
97
        # Let things settle a bit before moving forward
 
98
        time.sleep(30)
 
99
 
 
100
    def _get_rmq_sentry_units(self):
 
101
        """Local helper specific to this 3-node rmq series of tests."""
 
102
        return [self.rmq0_sentry,
 
103
                self.rmq1_sentry,
 
104
                self.rmq2_sentry]
 
105
 
 
106
    def _test_rmq_amqp_messages_all_units(self, sentry_units,
 
107
                                          ssl=False, port=None):
 
108
        """Reusable test to send amqp messages to every listed rmq unit
 
109
        and check every listed rmq unit for messages.
 
110
 
 
111
        :param sentry_units: list of sentry units
 
112
        :returns: None if successful.  Raise on error.
 
113
        """
 
114
 
 
115
        # Add test user if it does not already exist
 
116
        u.add_rmq_test_user(sentry_units)
 
117
 
 
118
        # Handle ssl
 
119
        if ssl:
 
120
            u.configure_rmq_ssl_on(sentry_units, self.d, port=port)
 
121
        else:
 
122
            u.configure_rmq_ssl_off(sentry_units, self.d)
 
123
 
 
124
        # Publish and get amqp messages in all possible unit combinations.
 
125
        # Qty of checks == (qty of units) ^ 2
 
126
        amqp_msg_counter = 1
 
127
        host_names = u.get_unit_hostnames(sentry_units)
 
128
 
 
129
        for dest_unit in sentry_units:
 
130
            dest_unit_name = dest_unit.info['unit_name']
 
131
            dest_unit_host = dest_unit.info['public-address']
 
132
            dest_unit_host_name = host_names[dest_unit_name]
 
133
 
 
134
            for check_unit in sentry_units:
 
135
                check_unit_name = check_unit.info['unit_name']
 
136
                check_unit_host = check_unit.info['public-address']
 
137
                check_unit_host_name = host_names[check_unit_name]
 
138
 
 
139
                amqp_msg_stamp = u.get_uuid_epoch_stamp()
 
140
                amqp_msg = ('Message {}@{} {}'.format(amqp_msg_counter,
 
141
                                                      dest_unit_host,
 
142
                                                      amqp_msg_stamp)).upper()
 
143
                # Publish amqp message
 
144
                u.log.debug('Publish message to: {} '
 
145
                            '({} {})'.format(dest_unit_host,
 
146
                                             dest_unit_name,
 
147
                                             dest_unit_host_name))
 
148
 
 
149
                u.publish_amqp_message_by_unit(dest_unit,
 
150
                                               amqp_msg, ssl=ssl,
 
151
                                               port=port)
 
152
 
 
153
                # Wait a bit before checking for message
 
154
                time.sleep(2)
 
155
 
 
156
                # Get amqp message
 
157
                u.log.debug('Get message from:   {} '
 
158
                            '({} {})'.format(check_unit_host,
 
159
                                             check_unit_name,
 
160
                                             check_unit_host_name))
 
161
 
 
162
                amqp_msg_rcvd = u.get_amqp_message_by_unit(check_unit,
 
163
                                                           ssl=ssl,
 
164
                                                           port=port)
 
165
 
 
166
                # Validate amqp message content
 
167
                if amqp_msg == amqp_msg_rcvd:
 
168
                    u.log.debug('Message {} received '
 
169
                                'OK.'.format(amqp_msg_counter))
 
170
                else:
 
171
                    u.log.error('Expected: {}'.format(amqp_msg))
 
172
                    u.log.error('Actual:   {}'.format(amqp_msg_rcvd))
 
173
                    msg = 'Message {} mismatch.'.format(amqp_msg_counter)
 
174
                    amulet.raise_status(amulet.FAIL, msg)
 
175
 
 
176
                amqp_msg_counter += 1
 
177
 
 
178
        # Delete the test user
 
179
        u.delete_rmq_test_user(sentry_units)
 
180
 
 
181
    def test_100_rmq_processes(self):
 
182
        """Verify that the expected service processes are running
 
183
        on each rabbitmq-server unit."""
 
184
 
 
185
        # Beam and epmd sometimes briefly have more than one PID,
 
186
        # True checks for at least 1.
 
187
        rmq_processes = {
 
188
            'beam': True,
 
189
            'epmd': True,
 
190
        }
 
191
 
 
192
        # Units with process names and PID quantities expected
 
193
        expected_processes = {
 
194
            self.rmq0_sentry: rmq_processes,
 
195
            self.rmq1_sentry: rmq_processes,
 
196
            self.rmq2_sentry: rmq_processes
 
197
        }
 
198
 
 
199
        actual_pids = u.get_unit_process_ids(expected_processes)
 
200
        ret = u.validate_unit_process_ids(expected_processes, actual_pids)
 
201
        if ret:
 
202
            amulet.raise_status(amulet.FAIL, msg=ret)
 
203
 
 
204
        u.log.info('OK\n')
 
205
 
 
206
    def test_102_services(self):
 
207
        """Verify that the expected services are running on the
 
208
           corresponding service units."""
 
209
        services = {
 
210
            self.rmq0_sentry: ['rabbitmq-server'],
 
211
            self.rmq1_sentry: ['rabbitmq-server'],
 
212
            self.rmq2_sentry: ['rabbitmq-server'],
 
213
            self.cinder_sentry: ['cinder-api',
 
214
                                 'cinder-scheduler',
 
215
                                 'cinder-volume'],
 
216
        }
 
217
        ret = u.validate_services_by_name(services)
 
218
        if ret:
 
219
            amulet.raise_status(amulet.FAIL, msg=ret)
 
220
 
 
221
        u.log.info('OK\n')
 
222
 
 
223
    def test_200_rmq_cinder_amqp_relation(self):
 
224
        """Verify the rabbitmq-server:cinder amqp relation data"""
 
225
        u.log.debug('Checking rmq:cinder amqp relation data...')
 
226
        unit = self.rmq0_sentry
 
227
        relation = ['amqp', 'cinder:amqp']
 
228
        expected = {
 
229
            'private-address': u.valid_ip,
 
230
            'password': u.not_null,
 
231
            'hostname': u.valid_ip
 
232
        }
 
233
        ret = u.validate_relation_data(unit, relation, expected)
 
234
        if ret:
 
235
            msg = u.relation_error('amqp cinder', ret)
 
236
            amulet.raise_status(amulet.FAIL, msg=msg)
 
237
 
 
238
        u.log.info('OK\n')
 
239
 
 
240
    def test_201_cinder_rmq_amqp_relation(self):
 
241
        """Verify the cinder:rabbitmq-server amqp relation data"""
 
242
        u.log.debug('Checking cinder:rmq amqp relation data...')
 
243
        unit = self.cinder_sentry
 
244
        relation = ['amqp', 'rabbitmq-server:amqp']
 
245
        expected = {
 
246
            'private-address': u.valid_ip,
 
247
            'vhost': 'openstack',
 
248
            'username': u.not_null
 
249
        }
 
250
        ret = u.validate_relation_data(unit, relation, expected)
 
251
        if ret:
 
252
            msg = u.relation_error('cinder amqp', ret)
 
253
            amulet.raise_status(amulet.FAIL, msg=msg)
 
254
 
 
255
        u.log.info('OK\n')
 
256
 
 
257
    def test_202_rmq_nrpe_ext_master_relation(self):
 
258
        """Verify rabbitmq-server:nrpe nrpe-external-master relation data"""
 
259
        u.log.debug('Checking rmq:nrpe external master relation data...')
 
260
        unit = self.rmq0_sentry
 
261
        relation = ['nrpe-external-master',
 
262
                    'nrpe:nrpe-external-master']
 
263
 
 
264
        mon_sub = ('monitors:\n  remote:\n    nrpe:\n      rabbitmq: '
 
265
                   '{command: check_rabbitmq}\n      rabbitmq_queue: '
 
266
                   '{command: check_rabbitmq_queue}\n')
 
267
 
 
268
        expected = {
 
269
            'private-address': u.valid_ip,
 
270
            'monitors': mon_sub
 
271
        }
 
272
 
 
273
        ret = u.validate_relation_data(unit, relation, expected)
 
274
        if ret:
 
275
            msg = u.relation_error('amqp nrpe', ret)
 
276
            amulet.raise_status(amulet.FAIL, msg=msg)
 
277
 
 
278
        u.log.info('OK\n')
 
279
 
 
280
    def test_203_nrpe_rmq_ext_master_relation(self):
 
281
        """Verify nrpe:rabbitmq-server nrpe-external-master relation data"""
 
282
        u.log.debug('Checking nrpe:rmq external master relation data...')
 
283
        unit = self.nrpe_sentry
 
284
        relation = ['nrpe-external-master',
 
285
                    'rabbitmq-server:nrpe-external-master']
 
286
 
 
287
        expected = {
 
288
            'private-address': u.valid_ip
 
289
        }
 
290
 
 
291
        ret = u.validate_relation_data(unit, relation, expected)
 
292
        if ret:
 
293
            msg = u.relation_error('nrpe amqp', ret)
 
294
            amulet.raise_status(amulet.FAIL, msg=msg)
 
295
 
 
296
        u.log.info('OK\n')
 
297
 
 
298
    def test_300_rmq_config(self):
 
299
        """Verify the data in the rabbitmq conf file."""
 
300
        conf = '/etc/rabbitmq/rabbitmq-env.conf'
 
301
        sentry_units = self._get_rmq_sentry_units()
 
302
        for unit in sentry_units:
 
303
            host_name = unit.file_contents('/etc/hostname').strip()
 
304
            u.log.debug('Checking rabbitmq config file data on '
 
305
                        '{} ({})...'.format(unit.info['unit_name'],
 
306
                                            host_name))
 
307
            expected = {
 
308
                'RABBITMQ_NODENAME': 'rabbit@{}'.format(host_name)
 
309
            }
 
310
 
 
311
            file_contents = unit.file_contents(conf)
 
312
            u.validate_sectionless_conf(file_contents, expected)
 
313
 
 
314
        u.log.info('OK\n')
 
315
 
 
316
    def test_400_rmq_cluster_running_nodes(self):
 
317
        """Verify that cluster status from each rmq juju unit shows
 
318
        every cluster node as a running member in that cluster."""
 
319
        u.log.debug('Checking that all units are in cluster_status '
 
320
                    'running nodes...')
 
321
 
 
322
        sentry_units = self._get_rmq_sentry_units()
 
323
 
 
324
        ret = u.validate_rmq_cluster_running_nodes(sentry_units)
 
325
        if ret:
 
326
            amulet.raise_status(amulet.FAIL, msg=ret)
 
327
 
 
328
        u.log.info('OK\n')
 
329
 
 
330
    def test_402_rmq_connect_with_ssl_off(self):
 
331
        """Verify successful non-ssl amqp connection to all units when
 
332
        charm config option for ssl is set False."""
 
333
        u.log.debug('Confirming that non-ssl connection succeeds when '
 
334
                    'ssl config is off...')
 
335
        sentry_units = self._get_rmq_sentry_units()
 
336
        u.add_rmq_test_user(sentry_units)
 
337
        u.configure_rmq_ssl_off(sentry_units, self.d)
 
338
 
 
339
        # Check amqp connection for all units, expect connections to succeed
 
340
        for unit in sentry_units:
 
341
            connection = u.connect_amqp_by_unit(unit, ssl=False, fatal=False)
 
342
            connection.close()
 
343
 
 
344
        u.delete_rmq_test_user(sentry_units)
 
345
        u.log.info('OK\n')
 
346
 
 
347
    def test_404_rmq_ssl_connect_with_ssl_off(self):
 
348
        """Verify unsuccessful ssl amqp connection to all units when
 
349
        charm config option for ssl is set False."""
 
350
        u.log.debug('Confirming that ssl connection fails when ssl '
 
351
                    'config is off...')
 
352
        sentry_units = self._get_rmq_sentry_units()
 
353
        u.add_rmq_test_user(sentry_units)
 
354
        u.configure_rmq_ssl_off(sentry_units, self.d)
 
355
 
 
356
        # Check ssl amqp connection for all units, expect connections to fail
 
357
        for unit in sentry_units:
 
358
            connection = u.connect_amqp_by_unit(unit, ssl=True,
 
359
                                                port=5971, fatal=False)
 
360
            if connection:
 
361
                connection.close()
 
362
                msg = 'SSL connection unexpectedly succeeded with ssl=off'
 
363
                amulet.raise_status(amulet.FAIL, msg)
 
364
 
 
365
        u.delete_rmq_test_user(sentry_units)
 
366
        u.log.info('OK - Confirmed that ssl connection attempt fails '
 
367
                   'when ssl config is off.')
 
368
 
 
369
    def test_406_rmq_amqp_messages_all_units_ssl_off(self):
 
370
        """Send amqp messages to every rmq unit and check every rmq unit
 
371
        for messages.  Standard amqp tcp port, no ssl."""
 
372
        u.log.debug('Checking amqp message publish/get on all units '
 
373
                    '(ssl off)...')
 
374
 
 
375
        sentry_units = self._get_rmq_sentry_units()
 
376
        self._test_rmq_amqp_messages_all_units(sentry_units, ssl=False)
 
377
        u.log.info('OK\n')
 
378
 
 
379
    def test_408_rmq_amqp_messages_all_units_ssl_on(self):
 
380
        """Send amqp messages with ssl enabled, to every rmq unit and
 
381
        check every rmq unit for messages.  Standard ssl tcp port."""
 
382
        u.log.debug('Checking amqp message publish/get on all units '
 
383
                    '(ssl on)...')
 
384
 
 
385
        sentry_units = self._get_rmq_sentry_units()
 
386
        self._test_rmq_amqp_messages_all_units(sentry_units,
 
387
                                               ssl=True, port=5671)
 
388
        u.log.info('OK\n')
 
389
 
 
390
    def test_410_rmq_amqp_messages_all_units_ssl_alt_port(self):
 
391
        """Send amqp messages with ssl on, to every rmq unit and check
 
392
        every rmq unit for messages.  Custom ssl tcp port."""
 
393
        u.log.debug('Checking amqp message publish/get on all units '
 
394
                    '(ssl on)...')
 
395
 
 
396
        sentry_units = self._get_rmq_sentry_units()
 
397
        self._test_rmq_amqp_messages_all_units(sentry_units,
 
398
                                               ssl=True, port=5999)
 
399
        u.log.info('OK\n')
 
400
 
 
401
    def test_412_rmq_management_plugin(self):
 
402
        """Enable and check management plugin."""
 
403
        u.log.debug('Checking tcp socket connect to management plugin '
 
404
                    'port on all rmq units...')
 
405
 
 
406
        sentry_units = self._get_rmq_sentry_units()
 
407
        mgmt_port = 15672
 
408
 
 
409
        # Enable management plugin
 
410
        u.log.debug('Enabling management_plugin charm config option...')
 
411
        config = {'management_plugin': 'True'}
 
412
        self.d.configure('rabbitmq-server', config)
 
413
 
 
414
        # Check tcp connect to management plugin port
 
415
        max_wait = 120
 
416
        tries = 0
 
417
        ret = u.port_knock_units(sentry_units, mgmt_port)
 
418
        while ret and tries < (max_wait / 12):
 
419
            time.sleep(12)
 
420
            u.log.debug('Attempt {}: {}'.format(tries, ret))
 
421
            ret = u.port_knock_units(sentry_units, mgmt_port)
 
422
            tries += 1
 
423
 
 
424
        if ret:
 
425
            amulet.raise_status(amulet.FAIL, ret)
 
426
        else:
 
427
            u.log.debug('Connect to all units (OK)\n')
 
428
 
 
429
        # Disable management plugin
 
430
        u.log.debug('Disabling management_plugin charm config option...')
 
431
        config = {'management_plugin': 'False'}
 
432
        self.d.configure('rabbitmq-server', config)
 
433
 
 
434
        # Negative check - tcp connect to management plugin port
 
435
        u.log.info('Expect tcp connect fail since charm config '
 
436
                   'option is disabled.')
 
437
        tries = 0
 
438
        ret = u.port_knock_units(sentry_units, mgmt_port, expect_success=False)
 
439
        while ret and tries < (max_wait / 12):
 
440
            time.sleep(12)
 
441
            u.log.debug('Attempt {}: {}'.format(tries, ret))
 
442
            ret = u.port_knock_units(sentry_units, mgmt_port,
 
443
                                     expect_success=False)
 
444
            tries += 1
 
445
 
 
446
        if ret:
 
447
            amulet.raise_status(amulet.FAIL, ret)
 
448
        else:
 
449
            u.log.info('Confirm mgmt port closed on all units (OK)\n')
 
450
 
 
451
    def test_414_rmq_nrpe_monitors(self):
 
452
        """Check rabbimq-server nrpe monitor basic functionality."""
 
453
        sentry_units = self._get_rmq_sentry_units()
 
454
        host_names = u.get_unit_hostnames(sentry_units)
 
455
 
 
456
        # check_rabbitmq monitor
 
457
        u.log.debug('Checking nrpe check_rabbitmq on units...')
 
458
        cmds = ['egrep -oh /usr/local.* /etc/nagios/nrpe.d/'
 
459
                'check_rabbitmq.cfg']
 
460
        ret = u.check_commands_on_units(cmds, sentry_units)
 
461
        if ret:
 
462
            amulet.raise_status(amulet.FAIL, msg=ret)
 
463
 
 
464
        u.log.debug('Sleeping 70s for 1m cron job to run...')
 
465
        time.sleep(70)
 
466
 
 
467
        # check_rabbitmq_queue monitor
 
468
        u.log.debug('Checking nrpe check_rabbitmq_queue on units...')
 
469
        cmds = ['egrep -oh /usr/local.* /etc/nagios/nrpe.d/'
 
470
                'check_rabbitmq_queue.cfg']
 
471
        ret = u.check_commands_on_units(cmds, sentry_units)
 
472
        if ret:
 
473
            amulet.raise_status(amulet.FAIL, msg=ret)
 
474
 
 
475
        # check dat file existence
 
476
        u.log.debug('Checking nrpe dat file existence on units...')
 
477
        for sentry_unit in sentry_units:
 
478
            unit_name = sentry_unit.info['unit_name']
 
479
            unit_host_name = host_names[unit_name]
 
480
 
 
481
            cmds = [
 
482
                'stat /var/lib/rabbitmq/data/{}_general_stats.dat'.format(
 
483
                    unit_host_name),
 
484
                'stat /var/lib/rabbitmq/data/{}_queue_stats.dat'.format(
 
485
                    unit_host_name)
 
486
            ]
 
487
 
 
488
            ret = u.check_commands_on_units(cmds, [sentry_unit])
 
489
            if ret:
 
490
                amulet.raise_status(amulet.FAIL, msg=ret)
 
491
 
 
492
        u.log.info('OK\n')