blob: 7cbfa682b295384b0fd6207b59298bcd510834ba [file] [log] [blame]
Sean Dague655e0af2014-05-29 09:00:22 -04001#!/usr/bin/env python
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
Joe H. Rahme61469fd2014-12-02 17:09:17 +010015"""Javelin is a tool for creating, verifying, and deleting a small set of
Sean Dague655e0af2014-05-29 09:00:22 -040016resources in a declarative way.
17
Joe H. Rahme61469fd2014-12-02 17:09:17 +010018Javelin is meant to be used as a way to validate quickly that resources can
19survive an upgrade process.
20
21Authentication
22--------------
23
24Javelin will be creating (and removing) users and tenants so it needs the admin
25credentials of your cloud to operate properly. The corresponding info can be
26given the usual way, either through CLI options or environment variables.
27
28You're probably familiar with these, but just in case::
29
30 +----------+------------------+----------------------+
31 | Param | CLI | Environment Variable |
32 +----------+------------------+----------------------+
33 | Username | --os-username | OS_USERNAME |
34 | Password | --os-password | OS_PASSWORD |
35 | Tenant | --os-tenant-name | OS_TENANT_NAME |
36 +----------+------------------+----------------------+
37
38
39Runtime Arguments
40-----------------
41
42**-m/--mode**: (Required) Has to be one of 'check', 'create' or 'destroy'. It
43indicates which actions javelin is going to perform.
44
45**-r/--resources**: (Required) The path to a YAML file describing the resources
46used by Javelin.
47
48**-d/--devstack-base**: (Required) The path to the devstack repo used to
49retrieve artefacts (like images) that will be referenced in the resource files.
50
51**-c/--config-file**: (Optional) The path to a valid Tempest config file
52describing your cloud. Javelin may use this to determine if certain services
53are enabled and modify its behavior accordingly.
54
55
56Resource file
57-------------
58
59The resource file is a valid YAML file describing the resources that will be
60created, checked and destroyed by javelin. Here's a canonical example of a
61resource file::
62
63 tenants:
64 - javelin
65 - discuss
66
67 users:
68 - name: javelin
69 pass: gungnir
70 tenant: javelin
71 - name: javelin2
72 pass: gungnir2
73 tenant: discuss
74
75 # resources that we want to create
76 images:
77 - name: javelin_cirros
78 owner: javelin
79 file: cirros-0.3.2-x86_64-blank.img
Joe H. Rahme4352d5d2015-03-09 17:41:18 +010080 disk_format: ami
81 container_format: ami
Joe H. Rahme61469fd2014-12-02 17:09:17 +010082 aki: cirros-0.3.2-x86_64-vmlinuz
83 ari: cirros-0.3.2-x86_64-initrd
84
85 servers:
86 - name: peltast
87 owner: javelin
88 flavor: m1.small
89 image: javelin_cirros
Joe H. Rahme9350a102015-03-29 17:39:20 +020090 floating_ip_pool: public
Joe H. Rahme61469fd2014-12-02 17:09:17 +010091 - name: hoplite
92 owner: javelin
93 flavor: m1.medium
94 image: javelin_cirros
95
96
97An important piece of the resource definition is the *owner* field, which is
98the user (that we've created) that is the owner of that resource. All
99operations on that resource will happen as that regular user to ensure that
100admin level access does not mask issues.
101
102The check phase will act like a unit test, using well known assert methods to
103verify that the correct resources exist.
104
Sean Dague655e0af2014-05-29 09:00:22 -0400105"""
106
Matthew Treinish96e9e882014-06-09 18:37:19 -0400107import argparse
Chris Dent51e76de2014-10-01 12:07:14 +0100108import collections
Chris Dent878f3782014-06-30 17:04:15 +0100109import datetime
Sean Dague655e0af2014-05-29 09:00:22 -0400110import os
111import sys
112import unittest
Sean Dague655e0af2014-05-29 09:00:22 -0400113
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200114import netaddr
Doug Hellmann583ce2c2015-03-11 14:55:46 +0000115from oslo_log import log as logging
116from oslo_utils import timeutils
Matthew Treinish71426682015-04-23 11:19:38 -0400117import six
andreafb8a52282015-03-19 22:21:54 +0000118from tempest_lib import auth
Masayuki Igawad9388762015-01-20 14:56:42 +0900119from tempest_lib import exceptions as lib_exc
Matthew Treinish96e9e882014-06-09 18:37:19 -0400120import yaml
Sean Dague655e0af2014-05-29 09:00:22 -0400121
Joe Gordon28a84ae2014-07-17 15:38:28 +0000122from tempest import config
Sean Dague655e0af2014-05-29 09:00:22 -0400123from tempest.services.compute.json import flavors_client
Emilien Macchic3e3e292015-03-11 17:42:08 -0400124from tempest.services.compute.json import floating_ips_client
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +0000125from tempest.services.compute.json import security_group_rules_client
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200126from tempest.services.compute.json import security_groups_client
Sean Dague655e0af2014-05-29 09:00:22 -0400127from tempest.services.compute.json import servers_client
Jamie Lennoxc429e6a2015-02-24 10:42:42 +1100128from tempest.services.identity.v2.json import identity_client
Sean Dague655e0af2014-05-29 09:00:22 -0400129from tempest.services.image.v2.json import image_client
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200130from tempest.services.network.json import network_client
Sean Dague655e0af2014-05-29 09:00:22 -0400131from tempest.services.object_storage import container_client
132from tempest.services.object_storage import object_client
Chris Dent878f3782014-06-30 17:04:15 +0100133from tempest.services.telemetry.json import telemetry_client
Emilien Macchi626b4f82014-06-15 21:44:29 +0200134from tempest.services.volume.json import volumes_client
Sean Dague655e0af2014-05-29 09:00:22 -0400135
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200136CONF = config.CONF
Sean Dague655e0af2014-05-29 09:00:22 -0400137OPTS = {}
138USERS = {}
Chris Dent51e76de2014-10-01 12:07:14 +0100139RES = collections.defaultdict(list)
Sean Dague655e0af2014-05-29 09:00:22 -0400140
141LOG = None
142
Chris Dent878f3782014-06-30 17:04:15 +0100143JAVELIN_START = datetime.datetime.utcnow()
144
Sean Dague655e0af2014-05-29 09:00:22 -0400145
146class OSClient(object):
147 _creds = None
148 identity = None
149 servers = None
150
151 def __init__(self, user, pw, tenant):
Ken'ichi Ohmichi4771cbc2015-01-19 23:45:23 +0000152 default_params = {
153 'disable_ssl_certificate_validation':
154 CONF.identity.disable_ssl_certificate_validation,
155 'ca_certs': CONF.identity.ca_certificates_file,
156 'trace_requests': CONF.debug.trace_requests
157 }
Ken'ichi Ohmichid5dba1c2015-01-23 02:23:22 +0000158 default_params_with_timeout_values = {
159 'build_interval': CONF.compute.build_interval,
160 'build_timeout': CONF.compute.build_timeout
161 }
162 default_params_with_timeout_values.update(default_params)
163
Ken'ichi Ohmichi4771cbc2015-01-19 23:45:23 +0000164 compute_params = {
165 'service': CONF.compute.catalog_type,
166 'region': CONF.compute.region or CONF.identity.region,
167 'endpoint_type': CONF.compute.endpoint_type,
168 'build_interval': CONF.compute.build_interval,
169 'build_timeout': CONF.compute.build_timeout
170 }
171 compute_params.update(default_params)
172
Ken'ichi Ohmichi564b2ad2015-01-22 02:08:59 +0000173 object_storage_params = {
174 'service': CONF.object_storage.catalog_type,
175 'region': CONF.object_storage.region or CONF.identity.region,
176 'endpoint_type': CONF.object_storage.endpoint_type
177 }
178 object_storage_params.update(default_params)
179
andreafb8a52282015-03-19 22:21:54 +0000180 _creds = auth.KeystoneV2Credentials(
Sean Dague655e0af2014-05-29 09:00:22 -0400181 username=user,
182 password=pw,
183 tenant_name=tenant)
Andrea Frittoli90012352015-02-25 21:58:02 +0000184 auth_provider_params = {
185 'disable_ssl_certificate_validation':
186 CONF.identity.disable_ssl_certificate_validation,
187 'ca_certs': CONF.identity.ca_certificates_file,
188 'trace_requests': CONF.debug.trace_requests
189 }
andreafb8a52282015-03-19 22:21:54 +0000190 _auth = auth.KeystoneV2AuthProvider(
Andrea Frittoli90012352015-02-25 21:58:02 +0000191 _creds, CONF.identity.uri, **auth_provider_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000192 self.identity = identity_client.IdentityClient(
ghanshyamd26b5cd2015-02-09 14:48:58 +0900193 _auth,
194 CONF.identity.catalog_type,
195 CONF.identity.region,
196 endpoint_type='adminURL',
197 **default_params_with_timeout_values)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000198 self.servers = servers_client.ServersClient(_auth,
199 **compute_params)
200 self.flavors = flavors_client.FlavorsClient(_auth,
201 **compute_params)
202 self.floating_ips = floating_ips_client.FloatingIPsClient(
Emilien Macchic3e3e292015-03-11 17:42:08 -0400203 _auth, **compute_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000204 self.secgroups = security_groups_client.SecurityGroupsClient(
Ken'ichi Ohmichi4771cbc2015-01-19 23:45:23 +0000205 _auth, **compute_params)
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +0000206 self.secrules = security_group_rules_client.SecurityGroupRulesClient(
207 _auth, **compute_params)
Ken'ichi Ohmichi564b2ad2015-01-22 02:08:59 +0000208 self.objects = object_client.ObjectClient(_auth,
209 **object_storage_params)
210 self.containers = container_client.ContainerClient(
211 _auth, **object_storage_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000212 self.images = image_client.ImageClientV2(
Masayuki Igawabc7e1892015-03-03 11:46:48 +0900213 _auth,
214 CONF.image.catalog_type,
215 CONF.image.region or CONF.identity.region,
216 endpoint_type=CONF.image.endpoint_type,
217 build_interval=CONF.image.build_interval,
218 build_timeout=CONF.image.build_timeout,
219 **default_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000220 self.telemetry = telemetry_client.TelemetryClient(
Ken'ichi Ohmichid5dba1c2015-01-23 02:23:22 +0000221 _auth,
222 CONF.telemetry.catalog_type,
223 CONF.identity.region,
224 endpoint_type=CONF.telemetry.endpoint_type,
225 **default_params_with_timeout_values)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000226 self.volumes = volumes_client.VolumesClient(
Ken'ichi Ohmichif85e9bd2015-01-27 12:51:47 +0000227 _auth,
228 CONF.volume.catalog_type,
229 CONF.volume.region or CONF.identity.region,
230 endpoint_type=CONF.volume.endpoint_type,
231 build_interval=CONF.volume.build_interval,
232 build_timeout=CONF.volume.build_timeout,
233 **default_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000234 self.networks = network_client.NetworkClient(
Ken'ichi Ohmichia182e862015-01-21 01:16:37 +0000235 _auth,
236 CONF.network.catalog_type,
237 CONF.network.region or CONF.identity.region,
238 endpoint_type=CONF.network.endpoint_type,
239 build_interval=CONF.network.build_interval,
240 build_timeout=CONF.network.build_timeout,
241 **default_params)
Sean Dague655e0af2014-05-29 09:00:22 -0400242
243
244def load_resources(fname):
Joe H. Rahme02736732015-01-27 18:33:09 +0100245 """Load the expected resources from a yaml file."""
Sean Dague655e0af2014-05-29 09:00:22 -0400246 return yaml.load(open(fname, 'r'))
247
248
249def keystone_admin():
250 return OSClient(OPTS.os_username, OPTS.os_password, OPTS.os_tenant_name)
251
252
253def client_for_user(name):
254 LOG.debug("Entering client_for_user")
255 if name in USERS:
256 user = USERS[name]
257 LOG.debug("Created client for user %s" % user)
258 return OSClient(user['name'], user['pass'], user['tenant'])
259 else:
260 LOG.error("%s not found in USERS: %s" % (name, USERS))
261
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200262
Sean Dague655e0af2014-05-29 09:00:22 -0400263###################
264#
265# TENANTS
266#
267###################
268
269
270def create_tenants(tenants):
271 """Create tenants from resource definition.
272
273 Don't create the tenants if they already exist.
274 """
275 admin = keystone_admin()
David Kranzb7afa922014-12-30 10:56:26 -0500276 body = admin.identity.list_tenants()
Sean Dague655e0af2014-05-29 09:00:22 -0400277 existing = [x['name'] for x in body]
278 for tenant in tenants:
279 if tenant not in existing:
280 admin.identity.create_tenant(tenant)
281 else:
282 LOG.warn("Tenant '%s' already exists in this environment" % tenant)
283
Emilien Macchibb71e072014-07-05 19:18:52 +0200284
285def destroy_tenants(tenants):
286 admin = keystone_admin()
287 for tenant in tenants:
288 tenant_id = admin.identity.get_tenant_by_name(tenant)['id']
David Kranzb7afa922014-12-30 10:56:26 -0500289 admin.identity.delete_tenant(tenant_id)
Emilien Macchibb71e072014-07-05 19:18:52 +0200290
Sean Dague655e0af2014-05-29 09:00:22 -0400291##############
292#
293# USERS
294#
295##############
296
297
298def _users_for_tenant(users, tenant):
299 u_for_t = []
300 for user in users:
301 for n in user:
302 if user[n]['tenant'] == tenant:
303 u_for_t.append(user[n])
304 return u_for_t
305
306
307def _tenants_from_users(users):
308 tenants = set()
309 for user in users:
310 for n in user:
311 tenants.add(user[n]['tenant'])
312 return tenants
313
314
Joe H. Rahme9c9fc4d2015-03-31 00:35:05 +0200315def _assign_swift_role(user, swift_role):
Sean Dague655e0af2014-05-29 09:00:22 -0400316 admin = keystone_admin()
David Kranzb7afa922014-12-30 10:56:26 -0500317 roles = admin.identity.list_roles()
Joe H. Rahme9c9fc4d2015-03-31 00:35:05 +0200318 role = next(r for r in roles if r['name'] == swift_role)
Sean Dague655e0af2014-05-29 09:00:22 -0400319 LOG.debug(USERS[user])
320 try:
321 admin.identity.assign_user_role(
322 USERS[user]['tenant_id'],
323 USERS[user]['id'],
324 role['id'])
Masayuki Igawad9388762015-01-20 14:56:42 +0900325 except lib_exc.Conflict:
Sean Dague655e0af2014-05-29 09:00:22 -0400326 # don't care if it's already assigned
327 pass
328
329
330def create_users(users):
331 """Create tenants from resource definition.
332
333 Don't create the tenants if they already exist.
334 """
335 global USERS
336 LOG.info("Creating users")
337 admin = keystone_admin()
338 for u in users:
339 try:
340 tenant = admin.identity.get_tenant_by_name(u['tenant'])
Masayuki Igawabfa07602015-01-20 18:47:17 +0900341 except lib_exc.NotFound:
Sean Dague655e0af2014-05-29 09:00:22 -0400342 LOG.error("Tenant: %s - not found" % u['tenant'])
343 continue
344 try:
345 admin.identity.get_user_by_username(tenant['id'], u['name'])
346 LOG.warn("User '%s' already exists in this environment"
347 % u['name'])
Masayuki Igawabfa07602015-01-20 18:47:17 +0900348 except lib_exc.NotFound:
Sean Dague655e0af2014-05-29 09:00:22 -0400349 admin.identity.create_user(
350 u['name'], u['pass'], tenant['id'],
351 "%s@%s" % (u['name'], tenant['id']),
352 enabled=True)
353
354
Emilien Macchibb71e072014-07-05 19:18:52 +0200355def destroy_users(users):
356 admin = keystone_admin()
357 for user in users:
Emilien Macchi436de862014-09-30 17:09:50 -0400358 tenant_id = admin.identity.get_tenant_by_name(user['tenant'])['id']
359 user_id = admin.identity.get_user_by_username(tenant_id,
360 user['name'])['id']
David Kranzb7afa922014-12-30 10:56:26 -0500361 admin.identity.delete_user(user_id)
Emilien Macchibb71e072014-07-05 19:18:52 +0200362
363
Sean Dague655e0af2014-05-29 09:00:22 -0400364def collect_users(users):
365 global USERS
Joe Gordon618c9fb2014-07-16 15:40:01 +0200366 LOG.info("Collecting users")
Sean Dague655e0af2014-05-29 09:00:22 -0400367 admin = keystone_admin()
368 for u in users:
369 tenant = admin.identity.get_tenant_by_name(u['tenant'])
370 u['tenant_id'] = tenant['id']
371 USERS[u['name']] = u
372 body = admin.identity.get_user_by_username(tenant['id'], u['name'])
373 USERS[u['name']]['id'] = body['id']
374
375
376class JavelinCheck(unittest.TestCase):
377 def __init__(self, users, resources):
378 super(JavelinCheck, self).__init__()
379 self.users = users
380 self.res = resources
381
382 def runTest(self, *args):
383 pass
384
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200385 def _ping_ip(self, ip_addr, count, namespace=None):
386 if namespace is None:
387 ping_cmd = "ping -c1 " + ip_addr
388 else:
389 ping_cmd = "sudo ip netns exec %s ping -c1 %s" % (namespace,
390 ip_addr)
391 for current in range(count):
392 return_code = os.system(ping_cmd)
393 if return_code is 0:
394 break
395 self.assertNotEqual(current, count - 1,
396 "Server is not pingable at %s" % ip_addr)
397
Sean Dague655e0af2014-05-29 09:00:22 -0400398 def check(self):
399 self.check_users()
400 self.check_objects()
401 self.check_servers()
Emilien Macchid18fec12014-09-15 14:32:54 -0400402 self.check_volumes()
Chris Dent878f3782014-06-30 17:04:15 +0100403 self.check_telemetry()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200404 self.check_secgroups()
405
406 # validate neutron is enabled and ironic disabled:
407 # Tenant network isolation is not supported when using ironic.
408 # "admin" has set up a neutron flat network environment within a shared
409 # fixed network for all tenants to use.
410 # In this case, network/subnet/router creation can be skipped and the
411 # server booted the same as nova network.
412 if (CONF.service_available.neutron and
413 not CONF.baremetal.driver_enabled):
414 self.check_networking()
Sean Dague655e0af2014-05-29 09:00:22 -0400415
416 def check_users(self):
417 """Check that the users we expect to exist, do.
418
419 We don't use the resource list for this because we need to validate
420 that things like tenantId didn't drift across versions.
421 """
Joe Gordon618c9fb2014-07-16 15:40:01 +0200422 LOG.info("checking users")
Matthew Treinish71426682015-04-23 11:19:38 -0400423 for name, user in six.iteritems(self.users):
Sean Dague655e0af2014-05-29 09:00:22 -0400424 client = keystone_admin()
David Kranzb7afa922014-12-30 10:56:26 -0500425 found = client.identity.get_user(user['id'])
Sean Dague655e0af2014-05-29 09:00:22 -0400426 self.assertEqual(found['name'], user['name'])
427 self.assertEqual(found['tenantId'], user['tenant_id'])
428
429 # also ensure we can auth with that user, and do something
430 # on the cloud. We don't care about the results except that it
431 # remains authorized.
432 client = client_for_user(user['name'])
David Kranzae99b9a2015-02-16 13:37:01 -0500433 client.servers.list_servers()
Sean Dague655e0af2014-05-29 09:00:22 -0400434
435 def check_objects(self):
436 """Check that the objects created are still there."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000437 if not self.res.get('objects'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000438 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200439 LOG.info("checking objects")
Sean Dague655e0af2014-05-29 09:00:22 -0400440 for obj in self.res['objects']:
441 client = client_for_user(obj['owner'])
442 r, contents = client.objects.get_object(
443 obj['container'], obj['name'])
444 source = _file_contents(obj['file'])
445 self.assertEqual(contents, source)
446
447 def check_servers(self):
448 """Check that the servers are still up and running."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000449 if not self.res.get('servers'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000450 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200451 LOG.info("checking servers")
Sean Dague655e0af2014-05-29 09:00:22 -0400452 for server in self.res['servers']:
453 client = client_for_user(server['owner'])
454 found = _get_server_by_name(client, server['name'])
455 self.assertIsNotNone(
456 found,
457 "Couldn't find expected server %s" % server['name'])
458
Ken'ichi Ohmichi76800242015-07-03 05:12:31 +0000459 found = client.servers.show_server(found['id'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200460 # validate neutron is enabled and ironic disabled:
461 if (CONF.service_available.neutron and
462 not CONF.baremetal.driver_enabled):
Emilien Macchic3e3e292015-03-11 17:42:08 -0400463 _floating_is_alive = False
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200464 for network_name, body in found['addresses'].items():
465 for addr in body:
466 ip = addr['addr']
Emilien Macchic3e3e292015-03-11 17:42:08 -0400467 # If floatingip_for_ssh is at True, it's assumed
468 # you want to use the floating IP to reach the server,
469 # fallback to fixed IP, then other type.
470 # This is useful in multi-node environment.
armando-migliaccio2a5ac822015-03-13 19:49:55 -0700471 if CONF.compute.use_floatingip_for_ssh:
Emilien Macchic3e3e292015-03-11 17:42:08 -0400472 if addr.get('OS-EXT-IPS:type',
473 'floating') == 'floating':
474 self._ping_ip(ip, 60)
475 _floating_is_alive = True
476 elif addr.get('OS-EXT-IPS:type', 'fixed') == 'fixed':
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200477 namespace = _get_router_namespace(client,
478 network_name)
479 self._ping_ip(ip, 60, namespace)
480 else:
481 self._ping_ip(ip, 60)
Emilien Macchic3e3e292015-03-11 17:42:08 -0400482 # if floatingip_for_ssh is at True, validate found a
483 # floating IP and ping worked.
armando-migliaccio2a5ac822015-03-13 19:49:55 -0700484 if CONF.compute.use_floatingip_for_ssh:
Emilien Macchic3e3e292015-03-11 17:42:08 -0400485 self.assertTrue(_floating_is_alive,
486 "Server %s has no floating IP." %
487 server['name'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200488 else:
489 addr = found['addresses']['private'][0]['addr']
490 self._ping_ip(addr, 60)
491
492 def check_secgroups(self):
Joe H. Rahme02736732015-01-27 18:33:09 +0100493 """Check that the security groups still exist."""
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200494 LOG.info("Checking security groups")
495 for secgroup in self.res['secgroups']:
496 client = client_for_user(secgroup['owner'])
497 found = _get_resource_by_name(client.secgroups, 'security_groups',
498 secgroup['name'])
499 self.assertIsNotNone(
500 found,
501 "Couldn't find expected secgroup %s" % secgroup['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400502
Chris Dent878f3782014-06-30 17:04:15 +0100503 def check_telemetry(self):
504 """Check that ceilometer provides a sane sample.
505
nayna-patel1dfbedb2015-08-04 11:07:56 +0000506 Confirm that there is more than one sample and that they have the
Chris Dent878f3782014-06-30 17:04:15 +0100507 expected metadata.
508
509 If in check mode confirm that the oldest sample available is from
510 before the upgrade.
511 """
Chris Dent0b76f2f2014-10-10 14:24:28 +0100512 if not self.res.get('telemetry'):
513 return
Chris Dent878f3782014-06-30 17:04:15 +0100514 LOG.info("checking telemetry")
515 for server in self.res['servers']:
516 client = client_for_user(server['owner'])
David Kranz20d06f42015-02-09 14:54:15 -0500517 body = client.telemetry.list_samples(
Chris Dent878f3782014-06-30 17:04:15 +0100518 'instance',
519 query=('metadata.display_name', 'eq', server['name'])
520 )
Chris Dent878f3782014-06-30 17:04:15 +0100521 self.assertTrue(len(body) >= 1, 'expecting at least one sample')
522 self._confirm_telemetry_sample(server, body[-1])
523
Emilien Macchi626b4f82014-06-15 21:44:29 +0200524 def check_volumes(self):
525 """Check that the volumes are still there and attached."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000526 if not self.res.get('volumes'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000527 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200528 LOG.info("checking volumes")
Emilien Macchi626b4f82014-06-15 21:44:29 +0200529 for volume in self.res['volumes']:
530 client = client_for_user(volume['owner'])
Emilien Macchid18fec12014-09-15 14:32:54 -0400531 vol_body = _get_volume_by_name(client, volume['name'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200532 self.assertIsNotNone(
Emilien Macchid18fec12014-09-15 14:32:54 -0400533 vol_body,
Emilien Macchi626b4f82014-06-15 21:44:29 +0200534 "Couldn't find expected volume %s" % volume['name'])
535
536 # Verify that a volume's attachment retrieved
537 server_id = _get_server_by_name(client, volume['server'])['id']
Emilien Macchid18fec12014-09-15 14:32:54 -0400538 attachment = client.volumes.get_attachment_from_volume(vol_body)
539 self.assertEqual(vol_body['id'], attachment['volume_id'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200540 self.assertEqual(server_id, attachment['server_id'])
541
Chris Dent878f3782014-06-30 17:04:15 +0100542 def _confirm_telemetry_sample(self, server, sample):
543 """Check this sample matches the expected resource metadata."""
544 # Confirm display_name
545 self.assertEqual(server['name'],
546 sample['resource_metadata']['display_name'])
547 # Confirm instance_type of flavor
548 flavor = sample['resource_metadata'].get(
549 'flavor.name',
550 sample['resource_metadata'].get('instance_type')
551 )
552 self.assertEqual(server['flavor'], flavor)
553 # Confirm the oldest sample was created before upgrade.
554 if OPTS.mode == 'check':
555 oldest_timestamp = timeutils.normalize_time(
556 timeutils.parse_isotime(sample['timestamp']))
557 self.assertTrue(
558 oldest_timestamp < JAVELIN_START,
559 'timestamp should come before start of second javelin run'
560 )
561
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200562 def check_networking(self):
563 """Check that the networks are still there."""
564 for res_type in ('networks', 'subnets', 'routers'):
565 for res in self.res[res_type]:
566 client = client_for_user(res['owner'])
567 found = _get_resource_by_name(client.networks, res_type,
568 res['name'])
569 self.assertIsNotNone(
570 found,
571 "Couldn't find expected resource %s" % res['name'])
572
Sean Dague655e0af2014-05-29 09:00:22 -0400573
574#######################
575#
576# OBJECTS
577#
578#######################
579
580
581def _file_contents(fname):
582 with open(fname, 'r') as f:
583 return f.read()
584
585
586def create_objects(objects):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000587 if not objects:
588 return
Sean Dague655e0af2014-05-29 09:00:22 -0400589 LOG.info("Creating objects")
590 for obj in objects:
591 LOG.debug("Object %s" % obj)
Joe H. Rahme9c9fc4d2015-03-31 00:35:05 +0200592 swift_role = obj.get('swift_role', 'Member')
593 _assign_swift_role(obj['owner'], swift_role)
Sean Dague655e0af2014-05-29 09:00:22 -0400594 client = client_for_user(obj['owner'])
595 client.containers.create_container(obj['container'])
596 client.objects.create_object(
597 obj['container'], obj['name'],
598 _file_contents(obj['file']))
599
Emilien Macchibb71e072014-07-05 19:18:52 +0200600
601def destroy_objects(objects):
602 for obj in objects:
603 client = client_for_user(obj['owner'])
604 r, body = client.objects.delete_object(obj['container'], obj['name'])
Emilien Macchid70f5102014-09-10 09:54:49 -0400605 if not (200 <= int(r['status']) < 299):
Emilien Macchibb71e072014-07-05 19:18:52 +0200606 raise ValueError("unable to destroy object: [%s] %s" % (r, body))
607
608
Sean Dague655e0af2014-05-29 09:00:22 -0400609#######################
610#
611# IMAGES
612#
613#######################
614
615
Sean Dague319b37a2014-07-11 07:28:11 -0400616def _resolve_image(image, imgtype):
617 name = image[imgtype]
618 fname = os.path.join(OPTS.devstack_base, image['imgdir'], name)
619 return name, fname
620
621
Joe Gordon6f0426c2014-07-25 01:10:28 +0000622def _get_image_by_name(client, name):
Ken'ichi Ohmichie3acc122015-05-22 00:32:54 +0000623 body = client.images.list_images()
Joe Gordon6f0426c2014-07-25 01:10:28 +0000624 for image in body:
625 if name == image['name']:
626 return image
627 return None
628
629
Sean Dague655e0af2014-05-29 09:00:22 -0400630def create_images(images):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000631 if not images:
632 return
Joe Gordona18d6862014-07-24 22:55:46 +0000633 LOG.info("Creating images")
Sean Dague655e0af2014-05-29 09:00:22 -0400634 for image in images:
635 client = client_for_user(image['owner'])
636
Joe H. Rahme4352d5d2015-03-09 17:41:18 +0100637 # DEPRECATED: 'format' was used for ami images
638 # Use 'disk_format' and 'container_format' instead
639 if 'format' in image:
640 LOG.warning("Deprecated: 'format' is deprecated for images "
641 "description. Please use 'disk_format' and 'container_"
642 "format' instead.")
643 image['disk_format'] = image['format']
644 image['container_format'] = image['format']
645
Sean Dague655e0af2014-05-29 09:00:22 -0400646 # only upload a new image if the name isn't there
Joe Gordon6f0426c2014-07-25 01:10:28 +0000647 if _get_image_by_name(client, image['name']):
Joe Gordona18d6862014-07-24 22:55:46 +0000648 LOG.info("Image '%s' already exists" % image['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400649 continue
650
651 # special handling for 3 part image
652 extras = {}
Joe H. Rahme4352d5d2015-03-09 17:41:18 +0100653 if image['disk_format'] == 'ami':
Sean Dague319b37a2014-07-11 07:28:11 -0400654 name, fname = _resolve_image(image, 'aki')
David Kranz34f18782015-01-06 13:43:55 -0500655 aki = client.images.create_image(
Sean Dague319b37a2014-07-11 07:28:11 -0400656 'javelin_' + name, 'aki', 'aki')
Ken'ichi Ohmichi66494e92015-06-08 04:28:10 +0000657 client.images.store_image_file(aki.get('id'), open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400658 extras['kernel_id'] = aki.get('id')
659
Sean Dague319b37a2014-07-11 07:28:11 -0400660 name, fname = _resolve_image(image, 'ari')
David Kranz34f18782015-01-06 13:43:55 -0500661 ari = client.images.create_image(
Sean Dague319b37a2014-07-11 07:28:11 -0400662 'javelin_' + name, 'ari', 'ari')
Ken'ichi Ohmichi66494e92015-06-08 04:28:10 +0000663 client.images.store_image_file(ari.get('id'), open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400664 extras['ramdisk_id'] = ari.get('id')
665
Sean Dague319b37a2014-07-11 07:28:11 -0400666 _, fname = _resolve_image(image, 'file')
David Kranz34f18782015-01-06 13:43:55 -0500667 body = client.images.create_image(
Joe H. Rahme4352d5d2015-03-09 17:41:18 +0100668 image['name'], image['container_format'],
669 image['disk_format'], **extras)
Sean Dague655e0af2014-05-29 09:00:22 -0400670 image_id = body.get('id')
Ken'ichi Ohmichi66494e92015-06-08 04:28:10 +0000671 client.images.store_image_file(image_id, open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400672
673
Joe Gordon6f0426c2014-07-25 01:10:28 +0000674def destroy_images(images):
675 if not images:
676 return
677 LOG.info("Destroying images")
678 for image in images:
679 client = client_for_user(image['owner'])
680
681 response = _get_image_by_name(client, image['name'])
682 if not response:
nayna-patel1dfbedb2015-08-04 11:07:56 +0000683 LOG.info("Image '%s' does not exist" % image['name'])
Joe Gordon6f0426c2014-07-25 01:10:28 +0000684 continue
685 client.images.delete_image(response['id'])
686
687
Sean Dague655e0af2014-05-29 09:00:22 -0400688#######################
689#
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200690# NETWORKS
691#
692#######################
693
694def _get_router_namespace(client, network):
695 network_id = _get_resource_by_name(client.networks,
696 'networks', network)['id']
David Kranz34e88122014-12-11 15:24:05 -0500697 n_body = client.networks.list_routers()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200698 for router in n_body['routers']:
699 router_id = router['id']
David Kranz34e88122014-12-11 15:24:05 -0500700 r_body = client.networks.list_router_interfaces(router_id)
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200701 for port in r_body['ports']:
702 if port['network_id'] == network_id:
703 return "qrouter-%s" % router_id
704
705
706def _get_resource_by_name(client, resource, name):
707 get_resources = getattr(client, 'list_%s' % resource)
708 if get_resources is None:
709 raise AttributeError("client doesn't have method list_%s" % resource)
David Kranz34e88122014-12-11 15:24:05 -0500710 # Until all tempest client methods are changed to return only one value,
711 # we cannot assume they all have the same signature so we need to discard
712 # the unused response first value it two values are being returned.
713 body = get_resources()
714 if type(body) == tuple:
715 body = body[1]
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200716 if isinstance(body, dict):
717 body = body[resource]
718 for res in body:
719 if name == res['name']:
720 return res
721 raise ValueError('%s not found in %s resources' % (name, resource))
722
723
724def create_networks(networks):
725 LOG.info("Creating networks")
726 for network in networks:
727 client = client_for_user(network['owner'])
728
729 # only create a network if the name isn't here
David Kranz34e88122014-12-11 15:24:05 -0500730 body = client.networks.list_networks()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200731 if any(item['name'] == network['name'] for item in body['networks']):
nayna-patel1dfbedb2015-08-04 11:07:56 +0000732 LOG.warning("Duplicated network name: %s" % network['name'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200733 continue
734
735 client.networks.create_network(name=network['name'])
736
737
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100738def destroy_networks(networks):
739 LOG.info("Destroying subnets")
740 for network in networks:
741 client = client_for_user(network['owner'])
742 network_id = _get_resource_by_name(client.networks, 'networks',
743 network['name'])['id']
744 client.networks.delete_network(network_id)
745
746
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200747def create_subnets(subnets):
748 LOG.info("Creating subnets")
749 for subnet in subnets:
750 client = client_for_user(subnet['owner'])
751
752 network = _get_resource_by_name(client.networks, 'networks',
753 subnet['network'])
754 ip_version = netaddr.IPNetwork(subnet['range']).version
755 # ensure we don't overlap with another subnet in the network
756 try:
757 client.networks.create_subnet(network_id=network['id'],
758 cidr=subnet['range'],
759 name=subnet['name'],
760 ip_version=ip_version)
Masayuki Igawa4b29e472015-02-16 10:41:54 +0900761 except lib_exc.BadRequest as e:
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200762 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
763 if not is_overlapping_cidr:
764 raise
765
766
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100767def destroy_subnets(subnets):
768 LOG.info("Destroying subnets")
769 for subnet in subnets:
770 client = client_for_user(subnet['owner'])
771 subnet_id = _get_resource_by_name(client.networks,
772 'subnets', subnet['name'])['id']
773 client.networks.delete_subnet(subnet_id)
774
775
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200776def create_routers(routers):
777 LOG.info("Creating routers")
778 for router in routers:
779 client = client_for_user(router['owner'])
780
781 # only create a router if the name isn't here
David Kranz34e88122014-12-11 15:24:05 -0500782 body = client.networks.list_routers()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200783 if any(item['name'] == router['name'] for item in body['routers']):
nayna-patel1dfbedb2015-08-04 11:07:56 +0000784 LOG.warning("Duplicated router name: %s" % router['name'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200785 continue
786
787 client.networks.create_router(router['name'])
788
789
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100790def destroy_routers(routers):
791 LOG.info("Destroying routers")
792 for router in routers:
793 client = client_for_user(router['owner'])
794 router_id = _get_resource_by_name(client.networks,
795 'routers', router['name'])['id']
796 for subnet in router['subnet']:
797 subnet_id = _get_resource_by_name(client.networks,
798 'subnets', subnet)['id']
799 client.networks.remove_router_interface_with_subnet_id(router_id,
800 subnet_id)
801 client.networks.delete_router(router_id)
802
803
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200804def add_router_interface(routers):
805 for router in routers:
806 client = client_for_user(router['owner'])
807 router_id = _get_resource_by_name(client.networks,
808 'routers', router['name'])['id']
809
810 for subnet in router['subnet']:
811 subnet_id = _get_resource_by_name(client.networks,
812 'subnets', subnet)['id']
813 # connect routers to their subnets
814 client.networks.add_router_interface_with_subnet_id(router_id,
815 subnet_id)
nayna-patel1dfbedb2015-08-04 11:07:56 +0000816 # connect routers to external network if set to "gateway"
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200817 if router['gateway']:
818 if CONF.network.public_network_id:
819 ext_net = CONF.network.public_network_id
820 client.networks._update_router(
821 router_id, set_enable_snat=True,
822 external_gateway_info={"network_id": ext_net})
823 else:
824 raise ValueError('public_network_id is not configured.')
825
826
827#######################
828#
Sean Dague655e0af2014-05-29 09:00:22 -0400829# SERVERS
830#
831#######################
832
833def _get_server_by_name(client, name):
David Kranzae99b9a2015-02-16 13:37:01 -0500834 body = client.servers.list_servers()
Sean Dague655e0af2014-05-29 09:00:22 -0400835 for server in body['servers']:
836 if name == server['name']:
837 return server
838 return None
839
840
Sean Dague655e0af2014-05-29 09:00:22 -0400841def _get_flavor_by_name(client, name):
David Kranz2fa77b22015-02-09 11:39:50 -0500842 body = client.flavors.list_flavors()
Sean Dague655e0af2014-05-29 09:00:22 -0400843 for flavor in body:
844 if name == flavor['name']:
845 return flavor
846 return None
847
848
849def create_servers(servers):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000850 if not servers:
851 return
Joe Gordona18d6862014-07-24 22:55:46 +0000852 LOG.info("Creating servers")
Sean Dague655e0af2014-05-29 09:00:22 -0400853 for server in servers:
854 client = client_for_user(server['owner'])
855
856 if _get_server_by_name(client, server['name']):
Joe Gordona18d6862014-07-24 22:55:46 +0000857 LOG.info("Server '%s' already exists" % server['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400858 continue
859
860 image_id = _get_image_by_name(client, server['image'])['id']
861 flavor_id = _get_flavor_by_name(client, server['flavor'])['id']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200862 # validate neutron is enabled and ironic disabled
863 kwargs = dict()
864 if (CONF.service_available.neutron and
865 not CONF.baremetal.driver_enabled and server.get('networks')):
866 get_net_id = lambda x: (_get_resource_by_name(
867 client.networks, 'networks', x)['id'])
868 kwargs['networks'] = [{'uuid': get_net_id(network)}
869 for network in server['networks']]
David Kranz0fb14292015-02-11 15:55:20 -0500870 body = client.servers.create_server(
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200871 server['name'], image_id, flavor_id, **kwargs)
Joe Gordon10f260b2014-07-24 23:27:19 +0000872 server_id = body['id']
873 client.servers.wait_for_server_status(server_id, 'ACTIVE')
nayna-patel1dfbedb2015-08-04 11:07:56 +0000874 # create security group(s) after server spawning
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200875 for secgroup in server['secgroups']:
876 client.servers.add_security_group(server_id, secgroup)
Emilien Macchic3e3e292015-03-11 17:42:08 -0400877 if CONF.compute.use_floatingip_for_ssh:
Joe H. Rahme9350a102015-03-29 17:39:20 +0200878 floating_ip_pool = server.get('floating_ip_pool')
879 floating_ip = client.floating_ips.create_floating_ip(
880 pool_name=floating_ip_pool)
Emilien Macchic3e3e292015-03-11 17:42:08 -0400881 client.floating_ips.associate_floating_ip_to_server(
882 floating_ip['ip'], server_id)
Sean Dague655e0af2014-05-29 09:00:22 -0400883
884
Joe Gordondb63b1c2014-07-24 23:21:21 +0000885def destroy_servers(servers):
886 if not servers:
887 return
888 LOG.info("Destroying servers")
889 for server in servers:
890 client = client_for_user(server['owner'])
891
Emilien Macchidc5bae22015-03-16 08:49:02 -0400892 response = _get_server_by_name(client, server['name'])
893 if not response:
Joe Gordondb63b1c2014-07-24 23:21:21 +0000894 LOG.info("Server '%s' does not exist" % server['name'])
895 continue
896
Emilien Macchidc5bae22015-03-16 08:49:02 -0400897 # TODO(EmilienM): disassociate floating IP from server and release it.
898 client.servers.delete_server(response['id'])
899 client.servers.wait_for_server_termination(response['id'],
Matthew Treinish1d14c542014-06-17 20:25:40 -0400900 ignore_error=True)
Joe Gordondb63b1c2014-07-24 23:21:21 +0000901
902
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200903def create_secgroups(secgroups):
904 LOG.info("Creating security groups")
905 for secgroup in secgroups:
906 client = client_for_user(secgroup['owner'])
907
908 # only create a security group if the name isn't here
909 # i.e. a security group may be used by another server
910 # only create a router if the name isn't here
David Kranz9964b4e2015-02-06 15:45:29 -0500911 body = client.secgroups.list_security_groups()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200912 if any(item['name'] == secgroup['name'] for item in body):
913 LOG.warning("Security group '%s' already exists" %
914 secgroup['name'])
915 continue
916
David Kranz9964b4e2015-02-06 15:45:29 -0500917 body = client.secgroups.create_security_group(
Ken'ichi Ohmichi34563cc2015-07-21 00:53:17 +0000918 name=secgroup['name'], description=secgroup['description'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200919 secgroup_id = body['id']
920 # for each security group, create the rules
921 for rule in secgroup['rules']:
922 ip_proto, from_port, to_port, cidr = rule.split()
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +0000923 client.secrules.create_security_group_rule(
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000924 parent_group_id=secgroup_id, ip_protocol=ip_proto,
925 from_port=from_port, to_port=to_port, cidr=cidr)
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200926
927
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100928def destroy_secgroups(secgroups):
929 LOG.info("Destroying security groups")
930 for secgroup in secgroups:
931 client = client_for_user(secgroup['owner'])
932 sg_id = _get_resource_by_name(client.secgroups,
933 'security_groups',
934 secgroup['name'])
935 # sg rules are deleted automatically
936 client.secgroups.delete_security_group(sg_id['id'])
937
938
Sean Dague655e0af2014-05-29 09:00:22 -0400939#######################
940#
Emilien Macchi626b4f82014-06-15 21:44:29 +0200941# VOLUMES
942#
943#######################
944
945def _get_volume_by_name(client, name):
John Warren6177c9e2015-08-19 20:00:17 +0000946 body = client.volumes.list_volumes()['volumes']
Emilien Macchid18fec12014-09-15 14:32:54 -0400947 for volume in body:
948 if name == volume['display_name']:
Emilien Macchi626b4f82014-06-15 21:44:29 +0200949 return volume
950 return None
951
952
953def create_volumes(volumes):
Chris Dent51e76de2014-10-01 12:07:14 +0100954 if not volumes:
955 return
956 LOG.info("Creating volumes")
Emilien Macchi626b4f82014-06-15 21:44:29 +0200957 for volume in volumes:
958 client = client_for_user(volume['owner'])
959
960 # only create a volume if the name isn't here
Emilien Macchid18fec12014-09-15 14:32:54 -0400961 if _get_volume_by_name(client, volume['name']):
962 LOG.info("volume '%s' already exists" % volume['name'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200963 continue
964
Emilien Macchid18fec12014-09-15 14:32:54 -0400965 size = volume['gb']
966 v_name = volume['name']
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000967 body = client.volumes.create_volume(size=size,
John Warren6177c9e2015-08-19 20:00:17 +0000968 display_name=v_name)['volume']
Emilien Macchid18fec12014-09-15 14:32:54 -0400969 client.volumes.wait_for_volume_status(body['id'], 'available')
Emilien Macchi626b4f82014-06-15 21:44:29 +0200970
971
Emilien Macchibb71e072014-07-05 19:18:52 +0200972def destroy_volumes(volumes):
973 for volume in volumes:
974 client = client_for_user(volume['owner'])
975 volume_id = _get_volume_by_name(client, volume['name'])['id']
Emilien Macchi5ebc27b2014-09-15 14:30:35 -0400976 client.volumes.detach_volume(volume_id)
977 client.volumes.delete_volume(volume_id)
Emilien Macchibb71e072014-07-05 19:18:52 +0200978
979
Emilien Macchi626b4f82014-06-15 21:44:29 +0200980def attach_volumes(volumes):
981 for volume in volumes:
982 client = client_for_user(volume['owner'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200983 server_id = _get_server_by_name(client, volume['server'])['id']
Emilien Macchid18fec12014-09-15 14:32:54 -0400984 volume_id = _get_volume_by_name(client, volume['name'])['id']
985 device = volume['device']
986 client.volumes.attach_volume(volume_id, server_id, device)
Emilien Macchi626b4f82014-06-15 21:44:29 +0200987
988
989#######################
990#
Sean Dague655e0af2014-05-29 09:00:22 -0400991# MAIN LOGIC
992#
993#######################
994
995def create_resources():
996 LOG.info("Creating Resources")
997 # first create keystone level resources, and we need to be admin
nayna-patel1dfbedb2015-08-04 11:07:56 +0000998 # for this.
Sean Dague655e0af2014-05-29 09:00:22 -0400999 create_tenants(RES['tenants'])
1000 create_users(RES['users'])
1001 collect_users(RES['users'])
1002
1003 # next create resources in a well known order
1004 create_objects(RES['objects'])
1005 create_images(RES['images'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +02001006
1007 # validate neutron is enabled and ironic is disabled
1008 if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
1009 create_networks(RES['networks'])
1010 create_subnets(RES['subnets'])
1011 create_routers(RES['routers'])
1012 add_router_interface(RES['routers'])
1013
1014 create_secgroups(RES['secgroups'])
Emilien Macchid18fec12014-09-15 14:32:54 -04001015 create_volumes(RES['volumes'])
Joe H. Rahmec96129b2015-03-30 11:23:31 +02001016
1017 # Only attempt attaching the volumes if servers are defined in the
nayna-patel1dfbedb2015-08-04 11:07:56 +00001018 # resource file
Joe H. Rahmec96129b2015-03-30 11:23:31 +02001019 if 'servers' in RES:
1020 create_servers(RES['servers'])
1021 attach_volumes(RES['volumes'])
Sean Dague655e0af2014-05-29 09:00:22 -04001022
1023
Joe Gordondb63b1c2014-07-24 23:21:21 +00001024def destroy_resources():
1025 LOG.info("Destroying Resources")
1026 # Destroy in inverse order of create
Joe Gordondb63b1c2014-07-24 23:21:21 +00001027 destroy_servers(RES['servers'])
Joe Gordon6f0426c2014-07-25 01:10:28 +00001028 destroy_images(RES['images'])
Emilien Macchibb71e072014-07-05 19:18:52 +02001029 destroy_objects(RES['objects'])
Emilien Macchibb71e072014-07-05 19:18:52 +02001030 destroy_volumes(RES['volumes'])
Jakub Libosvar3791ac92014-11-11 13:23:44 +01001031 if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
1032 destroy_routers(RES['routers'])
1033 destroy_subnets(RES['subnets'])
1034 destroy_networks(RES['networks'])
1035 destroy_secgroups(RES['secgroups'])
Emilien Macchibb71e072014-07-05 19:18:52 +02001036 destroy_users(RES['users'])
1037 destroy_tenants(RES['tenants'])
Joe Gordon6f0426c2014-07-25 01:10:28 +00001038 LOG.warn("Destroy mode incomplete")
1039
Joe Gordondb63b1c2014-07-24 23:21:21 +00001040
Sean Dague655e0af2014-05-29 09:00:22 -04001041def get_options():
1042 global OPTS
1043 parser = argparse.ArgumentParser(
1044 description='Create and validate a fixed set of OpenStack resources')
1045 parser.add_argument('-m', '--mode',
1046 metavar='<create|check|destroy>',
1047 required=True,
1048 help=('One of (create, check, destroy)'))
1049 parser.add_argument('-r', '--resources',
1050 required=True,
1051 metavar='resourcefile.yaml',
1052 help='Resources definition yaml file')
Joe Gordon28a84ae2014-07-17 15:38:28 +00001053
Sean Dague319b37a2014-07-11 07:28:11 -04001054 parser.add_argument(
1055 '-d', '--devstack-base',
1056 required=True,
1057 metavar='/opt/stack/old',
1058 help='Devstack base directory for retrieving artifacts')
Joe Gordon28a84ae2014-07-17 15:38:28 +00001059 parser.add_argument(
1060 '-c', '--config-file',
1061 metavar='/etc/tempest.conf',
1062 help='path to javelin2(tempest) config file')
1063
Sean Dague655e0af2014-05-29 09:00:22 -04001064 # auth bits, letting us also just source the devstack openrc
1065 parser.add_argument('--os-username',
1066 metavar='<auth-user-name>',
1067 default=os.environ.get('OS_USERNAME'),
1068 help=('Defaults to env[OS_USERNAME].'))
1069 parser.add_argument('--os-password',
1070 metavar='<auth-password>',
1071 default=os.environ.get('OS_PASSWORD'),
1072 help=('Defaults to env[OS_PASSWORD].'))
1073 parser.add_argument('--os-tenant-name',
1074 metavar='<auth-tenant-name>',
1075 default=os.environ.get('OS_TENANT_NAME'),
1076 help=('Defaults to env[OS_TENANT_NAME].'))
1077
1078 OPTS = parser.parse_args()
1079 if OPTS.mode not in ('create', 'check', 'destroy'):
1080 print("ERROR: Unknown mode -m %s\n" % OPTS.mode)
1081 parser.print_help()
1082 sys.exit(1)
Joe Gordon28a84ae2014-07-17 15:38:28 +00001083 if OPTS.config_file:
1084 config.CONF.set_config_path(OPTS.config_file)
Sean Dague655e0af2014-05-29 09:00:22 -04001085
1086
Joe Gordon915eb8e2014-07-17 11:25:46 +02001087def setup_logging():
Sean Dague655e0af2014-05-29 09:00:22 -04001088 global LOG
Doug Hellmann583ce2c2015-03-11 14:55:46 +00001089 logging.setup(CONF, __name__)
Sean Dague655e0af2014-05-29 09:00:22 -04001090 LOG = logging.getLogger(__name__)
Sean Dague655e0af2014-05-29 09:00:22 -04001091
1092
1093def main():
1094 global RES
1095 get_options()
1096 setup_logging()
Chris Dent51e76de2014-10-01 12:07:14 +01001097 RES.update(load_resources(OPTS.resources))
Sean Dague655e0af2014-05-29 09:00:22 -04001098
1099 if OPTS.mode == 'create':
1100 create_resources()
Joe Gordon1a097002014-07-24 23:44:08 +00001101 # Make sure the resources we just created actually work
1102 checker = JavelinCheck(USERS, RES)
1103 checker.check()
Sean Dague655e0af2014-05-29 09:00:22 -04001104 elif OPTS.mode == 'check':
1105 collect_users(RES['users'])
1106 checker = JavelinCheck(USERS, RES)
1107 checker.check()
1108 elif OPTS.mode == 'destroy':
Joe Gordondb63b1c2014-07-24 23:21:21 +00001109 collect_users(RES['users'])
1110 destroy_resources()
Sean Dague655e0af2014-05-29 09:00:22 -04001111 else:
1112 LOG.error('Unknown mode %s' % OPTS.mode)
1113 return 1
Joe Gordon246353a2014-07-18 00:10:28 +02001114 LOG.info('javelin2 successfully finished')
Sean Dague655e0af2014-05-29 09:00:22 -04001115 return 0
1116
1117if __name__ == "__main__":
1118 sys.exit(main())