blob: cdd0044c67ea703291d263d9bd128b5ebd4a0609 [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
ghanshyam2f6f5f12015-10-14 14:27:36 +0900120from tempest_lib.services.compute import flavors_client
ghanshyam94ef6492015-11-25 16:43:26 +0900121from tempest_lib.services.compute import security_groups_client
Matthew Treinish96e9e882014-06-09 18:37:19 -0400122import yaml
Sean Dague655e0af2014-05-29 09:00:22 -0400123
Ken'ichi Ohmichi6ea3f982015-11-09 12:41:13 +0000124from tempest.common import identity
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000125from tempest.common import waiters
Joe Gordon28a84ae2014-07-17 15:38:28 +0000126from tempest import config
Emilien Macchic3e3e292015-03-11 17:42:08 -0400127from tempest.services.compute.json import floating_ips_client
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +0000128from tempest.services.compute.json import security_group_rules_client
Sean Dague655e0af2014-05-29 09:00:22 -0400129from tempest.services.compute.json import servers_client
Jamie Lennoxc429e6a2015-02-24 10:42:42 +1100130from tempest.services.identity.v2.json import identity_client
Daniel Mellado6b16b922015-12-07 12:43:08 +0000131from tempest.services.identity.v2.json import roles_client
Daniel Melladob04da902015-11-20 17:43:12 +0100132from tempest.services.identity.v2.json import tenants_client
Ken'ichi Ohmichi69dcf442015-11-30 11:48:01 +0000133from tempest.services.image.v2.json import images_client
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200134from tempest.services.network.json import network_client
John Warren3961acd2015-10-02 14:38:53 -0400135from tempest.services.network.json import subnets_client
Sean Dague655e0af2014-05-29 09:00:22 -0400136from tempest.services.object_storage import container_client
137from tempest.services.object_storage import object_client
liu-sheng67b730e2015-07-16 15:19:33 +0800138from tempest.services.telemetry.json import alarming_client
Chris Dent878f3782014-06-30 17:04:15 +0100139from tempest.services.telemetry.json import telemetry_client
Yaroslav Lobankovdb4a2e12015-11-28 20:04:54 +0300140from tempest.services.volume.v1.json import volumes_client
Sean Dague655e0af2014-05-29 09:00:22 -0400141
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200142CONF = config.CONF
Sean Dague655e0af2014-05-29 09:00:22 -0400143OPTS = {}
144USERS = {}
Chris Dent51e76de2014-10-01 12:07:14 +0100145RES = collections.defaultdict(list)
Sean Dague655e0af2014-05-29 09:00:22 -0400146
147LOG = None
148
Chris Dent878f3782014-06-30 17:04:15 +0100149JAVELIN_START = datetime.datetime.utcnow()
150
Sean Dague655e0af2014-05-29 09:00:22 -0400151
152class OSClient(object):
153 _creds = None
154 identity = None
155 servers = None
156
157 def __init__(self, user, pw, tenant):
Ken'ichi Ohmichi4771cbc2015-01-19 23:45:23 +0000158 default_params = {
159 'disable_ssl_certificate_validation':
160 CONF.identity.disable_ssl_certificate_validation,
161 'ca_certs': CONF.identity.ca_certificates_file,
162 'trace_requests': CONF.debug.trace_requests
163 }
Ken'ichi Ohmichid5dba1c2015-01-23 02:23:22 +0000164 default_params_with_timeout_values = {
165 'build_interval': CONF.compute.build_interval,
166 'build_timeout': CONF.compute.build_timeout
167 }
168 default_params_with_timeout_values.update(default_params)
169
Ken'ichi Ohmichi4771cbc2015-01-19 23:45:23 +0000170 compute_params = {
171 'service': CONF.compute.catalog_type,
172 'region': CONF.compute.region or CONF.identity.region,
173 'endpoint_type': CONF.compute.endpoint_type,
174 'build_interval': CONF.compute.build_interval,
175 'build_timeout': CONF.compute.build_timeout
176 }
177 compute_params.update(default_params)
178
Ken'ichi Ohmichi564b2ad2015-01-22 02:08:59 +0000179 object_storage_params = {
180 'service': CONF.object_storage.catalog_type,
181 'region': CONF.object_storage.region or CONF.identity.region,
182 'endpoint_type': CONF.object_storage.endpoint_type
183 }
184 object_storage_params.update(default_params)
185
andreafb8a52282015-03-19 22:21:54 +0000186 _creds = auth.KeystoneV2Credentials(
Sean Dague655e0af2014-05-29 09:00:22 -0400187 username=user,
188 password=pw,
189 tenant_name=tenant)
Andrea Frittoli90012352015-02-25 21:58:02 +0000190 auth_provider_params = {
191 'disable_ssl_certificate_validation':
192 CONF.identity.disable_ssl_certificate_validation,
193 'ca_certs': CONF.identity.ca_certificates_file,
194 'trace_requests': CONF.debug.trace_requests
195 }
andreafb8a52282015-03-19 22:21:54 +0000196 _auth = auth.KeystoneV2AuthProvider(
Andrea Frittoli90012352015-02-25 21:58:02 +0000197 _creds, CONF.identity.uri, **auth_provider_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000198 self.identity = identity_client.IdentityClient(
ghanshyamd26b5cd2015-02-09 14:48:58 +0900199 _auth,
200 CONF.identity.catalog_type,
201 CONF.identity.region,
202 endpoint_type='adminURL',
203 **default_params_with_timeout_values)
Daniel Melladob04da902015-11-20 17:43:12 +0100204 self.tenants = tenants_client.TenantsClient(
205 _auth,
206 CONF.identity.catalog_type,
207 CONF.identity.region,
208 endpoint_type='adminURL',
209 **default_params_with_timeout_values)
Daniel Mellado6b16b922015-12-07 12:43:08 +0000210 self.roles = roles_client.RolesClient(
211 _auth,
212 CONF.identity.catalog_type,
213 CONF.identity.region,
214 endpoint_type='adminURL',
215 **default_params_with_timeout_values)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000216 self.servers = servers_client.ServersClient(_auth,
217 **compute_params)
218 self.flavors = flavors_client.FlavorsClient(_auth,
219 **compute_params)
220 self.floating_ips = floating_ips_client.FloatingIPsClient(
Emilien Macchic3e3e292015-03-11 17:42:08 -0400221 _auth, **compute_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000222 self.secgroups = security_groups_client.SecurityGroupsClient(
Ken'ichi Ohmichi4771cbc2015-01-19 23:45:23 +0000223 _auth, **compute_params)
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +0000224 self.secrules = security_group_rules_client.SecurityGroupRulesClient(
225 _auth, **compute_params)
Ken'ichi Ohmichi564b2ad2015-01-22 02:08:59 +0000226 self.objects = object_client.ObjectClient(_auth,
227 **object_storage_params)
228 self.containers = container_client.ContainerClient(
229 _auth, **object_storage_params)
Ken'ichi Ohmichi69dcf442015-11-30 11:48:01 +0000230 self.images = images_client.ImagesClientV2(
Masayuki Igawabc7e1892015-03-03 11:46:48 +0900231 _auth,
232 CONF.image.catalog_type,
233 CONF.image.region or CONF.identity.region,
234 endpoint_type=CONF.image.endpoint_type,
235 build_interval=CONF.image.build_interval,
236 build_timeout=CONF.image.build_timeout,
237 **default_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000238 self.telemetry = telemetry_client.TelemetryClient(
Ken'ichi Ohmichid5dba1c2015-01-23 02:23:22 +0000239 _auth,
240 CONF.telemetry.catalog_type,
241 CONF.identity.region,
242 endpoint_type=CONF.telemetry.endpoint_type,
243 **default_params_with_timeout_values)
liu-sheng67b730e2015-07-16 15:19:33 +0800244 self.alarming = alarming_client.AlarmingClient(
245 _auth,
246 CONF.alarm.catalog_type,
247 CONF.identity.region,
248 endpoint_type=CONF.alarm.endpoint_type,
249 **default_params_with_timeout_values)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000250 self.volumes = volumes_client.VolumesClient(
Ken'ichi Ohmichif85e9bd2015-01-27 12:51:47 +0000251 _auth,
252 CONF.volume.catalog_type,
253 CONF.volume.region or CONF.identity.region,
254 endpoint_type=CONF.volume.endpoint_type,
255 build_interval=CONF.volume.build_interval,
256 build_timeout=CONF.volume.build_timeout,
257 **default_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000258 self.networks = network_client.NetworkClient(
Ken'ichi Ohmichia182e862015-01-21 01:16:37 +0000259 _auth,
260 CONF.network.catalog_type,
261 CONF.network.region or CONF.identity.region,
262 endpoint_type=CONF.network.endpoint_type,
263 build_interval=CONF.network.build_interval,
264 build_timeout=CONF.network.build_timeout,
265 **default_params)
John Warren3961acd2015-10-02 14:38:53 -0400266 self.subnets = subnets_client.SubnetsClient(
267 _auth,
268 CONF.network.catalog_type,
269 CONF.network.region or CONF.identity.region,
270 endpoint_type=CONF.network.endpoint_type,
271 build_interval=CONF.network.build_interval,
272 build_timeout=CONF.network.build_timeout,
273 **default_params)
Sean Dague655e0af2014-05-29 09:00:22 -0400274
275
276def load_resources(fname):
Joe H. Rahme02736732015-01-27 18:33:09 +0100277 """Load the expected resources from a yaml file."""
Sean Dague655e0af2014-05-29 09:00:22 -0400278 return yaml.load(open(fname, 'r'))
279
280
281def keystone_admin():
282 return OSClient(OPTS.os_username, OPTS.os_password, OPTS.os_tenant_name)
283
284
285def client_for_user(name):
286 LOG.debug("Entering client_for_user")
287 if name in USERS:
288 user = USERS[name]
289 LOG.debug("Created client for user %s" % user)
290 return OSClient(user['name'], user['pass'], user['tenant'])
291 else:
292 LOG.error("%s not found in USERS: %s" % (name, USERS))
293
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200294
Sean Dague655e0af2014-05-29 09:00:22 -0400295###################
296#
297# TENANTS
298#
299###################
300
301
302def create_tenants(tenants):
303 """Create tenants from resource definition.
304
305 Don't create the tenants if they already exist.
306 """
307 admin = keystone_admin()
Daniel Melladob04da902015-11-20 17:43:12 +0100308 body = admin.tenants.list_tenants()['tenants']
Sean Dague655e0af2014-05-29 09:00:22 -0400309 existing = [x['name'] for x in body]
310 for tenant in tenants:
311 if tenant not in existing:
Daniel Melladob04da902015-11-20 17:43:12 +0100312 admin.tenants.create_tenant(tenant)['tenant']
Sean Dague655e0af2014-05-29 09:00:22 -0400313 else:
314 LOG.warn("Tenant '%s' already exists in this environment" % tenant)
315
Emilien Macchibb71e072014-07-05 19:18:52 +0200316
317def destroy_tenants(tenants):
318 admin = keystone_admin()
319 for tenant in tenants:
Daniel Melladob04da902015-11-20 17:43:12 +0100320 tenant_id = identity.get_tenant_by_name(admin.tenant, tenant)['id']
321 admin.tenants.delete_tenant(tenant_id)
Emilien Macchibb71e072014-07-05 19:18:52 +0200322
Sean Dague655e0af2014-05-29 09:00:22 -0400323##############
324#
325# USERS
326#
327##############
328
329
330def _users_for_tenant(users, tenant):
331 u_for_t = []
332 for user in users:
333 for n in user:
334 if user[n]['tenant'] == tenant:
335 u_for_t.append(user[n])
336 return u_for_t
337
338
339def _tenants_from_users(users):
340 tenants = set()
341 for user in users:
342 for n in user:
343 tenants.add(user[n]['tenant'])
344 return tenants
345
346
Joe H. Rahme9c9fc4d2015-03-31 00:35:05 +0200347def _assign_swift_role(user, swift_role):
Sean Dague655e0af2014-05-29 09:00:22 -0400348 admin = keystone_admin()
Daniel Mellado6b16b922015-12-07 12:43:08 +0000349 roles = admin.roles.list_roles()
Joe H. Rahme9c9fc4d2015-03-31 00:35:05 +0200350 role = next(r for r in roles if r['name'] == swift_role)
Sean Dague655e0af2014-05-29 09:00:22 -0400351 LOG.debug(USERS[user])
352 try:
Daniel Mellado6b16b922015-12-07 12:43:08 +0000353 admin.roles.assign_user_role(
Sean Dague655e0af2014-05-29 09:00:22 -0400354 USERS[user]['tenant_id'],
355 USERS[user]['id'],
356 role['id'])
Masayuki Igawad9388762015-01-20 14:56:42 +0900357 except lib_exc.Conflict:
Sean Dague655e0af2014-05-29 09:00:22 -0400358 # don't care if it's already assigned
359 pass
360
361
362def create_users(users):
363 """Create tenants from resource definition.
364
365 Don't create the tenants if they already exist.
366 """
367 global USERS
368 LOG.info("Creating users")
369 admin = keystone_admin()
370 for u in users:
371 try:
Daniel Melladob04da902015-11-20 17:43:12 +0100372 tenant = identity.get_tenant_by_name(admin.tenants, u['tenant'])
Masayuki Igawabfa07602015-01-20 18:47:17 +0900373 except lib_exc.NotFound:
Sean Dague655e0af2014-05-29 09:00:22 -0400374 LOG.error("Tenant: %s - not found" % u['tenant'])
375 continue
376 try:
Ken'ichi Ohmichid9fed312015-11-09 13:05:32 +0000377 identity.get_user_by_username(admin.identity,
378 tenant['id'], u['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400379 LOG.warn("User '%s' already exists in this environment"
380 % u['name'])
Masayuki Igawabfa07602015-01-20 18:47:17 +0900381 except lib_exc.NotFound:
Sean Dague655e0af2014-05-29 09:00:22 -0400382 admin.identity.create_user(
383 u['name'], u['pass'], tenant['id'],
384 "%s@%s" % (u['name'], tenant['id']),
385 enabled=True)
386
387
Emilien Macchibb71e072014-07-05 19:18:52 +0200388def destroy_users(users):
389 admin = keystone_admin()
390 for user in users:
Daniel Melladob04da902015-11-20 17:43:12 +0100391 tenant_id = identity.get_tenant_by_name(admin.tenants,
Ken'ichi Ohmichi6ea3f982015-11-09 12:41:13 +0000392 user['tenant'])['id']
Ken'ichi Ohmichid9fed312015-11-09 13:05:32 +0000393 user_id = identity.get_user_by_username(admin.identity,
394 tenant_id, user['name'])['id']
David Kranzb7afa922014-12-30 10:56:26 -0500395 admin.identity.delete_user(user_id)
Emilien Macchibb71e072014-07-05 19:18:52 +0200396
397
Sean Dague655e0af2014-05-29 09:00:22 -0400398def collect_users(users):
399 global USERS
Joe Gordon618c9fb2014-07-16 15:40:01 +0200400 LOG.info("Collecting users")
Sean Dague655e0af2014-05-29 09:00:22 -0400401 admin = keystone_admin()
402 for u in users:
Daniel Melladob04da902015-11-20 17:43:12 +0100403 tenant = identity.get_tenant_by_name(admin.tenants, u['tenant'])
Sean Dague655e0af2014-05-29 09:00:22 -0400404 u['tenant_id'] = tenant['id']
405 USERS[u['name']] = u
Ken'ichi Ohmichid9fed312015-11-09 13:05:32 +0000406 body = identity.get_user_by_username(admin.identity,
407 tenant['id'], u['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400408 USERS[u['name']]['id'] = body['id']
409
410
411class JavelinCheck(unittest.TestCase):
412 def __init__(self, users, resources):
413 super(JavelinCheck, self).__init__()
414 self.users = users
415 self.res = resources
416
417 def runTest(self, *args):
418 pass
419
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200420 def _ping_ip(self, ip_addr, count, namespace=None):
421 if namespace is None:
422 ping_cmd = "ping -c1 " + ip_addr
423 else:
424 ping_cmd = "sudo ip netns exec %s ping -c1 %s" % (namespace,
425 ip_addr)
426 for current in range(count):
427 return_code = os.system(ping_cmd)
428 if return_code is 0:
429 break
430 self.assertNotEqual(current, count - 1,
431 "Server is not pingable at %s" % ip_addr)
432
Sean Dague655e0af2014-05-29 09:00:22 -0400433 def check(self):
434 self.check_users()
435 self.check_objects()
436 self.check_servers()
Emilien Macchid18fec12014-09-15 14:32:54 -0400437 self.check_volumes()
Chris Dent878f3782014-06-30 17:04:15 +0100438 self.check_telemetry()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200439 self.check_secgroups()
440
441 # validate neutron is enabled and ironic disabled:
442 # Tenant network isolation is not supported when using ironic.
443 # "admin" has set up a neutron flat network environment within a shared
444 # fixed network for all tenants to use.
445 # In this case, network/subnet/router creation can be skipped and the
446 # server booted the same as nova network.
447 if (CONF.service_available.neutron and
448 not CONF.baremetal.driver_enabled):
449 self.check_networking()
Sean Dague655e0af2014-05-29 09:00:22 -0400450
451 def check_users(self):
452 """Check that the users we expect to exist, do.
453
454 We don't use the resource list for this because we need to validate
455 that things like tenantId didn't drift across versions.
456 """
Joe Gordon618c9fb2014-07-16 15:40:01 +0200457 LOG.info("checking users")
Matthew Treinish71426682015-04-23 11:19:38 -0400458 for name, user in six.iteritems(self.users):
Sean Dague655e0af2014-05-29 09:00:22 -0400459 client = keystone_admin()
Ken'ichi Ohmichi402b8752015-11-09 10:47:16 +0000460 found = client.identity.show_user(user['id'])['user']
Sean Dague655e0af2014-05-29 09:00:22 -0400461 self.assertEqual(found['name'], user['name'])
462 self.assertEqual(found['tenantId'], user['tenant_id'])
463
464 # also ensure we can auth with that user, and do something
465 # on the cloud. We don't care about the results except that it
466 # remains authorized.
467 client = client_for_user(user['name'])
David Kranzae99b9a2015-02-16 13:37:01 -0500468 client.servers.list_servers()
Sean Dague655e0af2014-05-29 09:00:22 -0400469
470 def check_objects(self):
471 """Check that the objects created are still there."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000472 if not self.res.get('objects'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000473 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200474 LOG.info("checking objects")
Sean Dague655e0af2014-05-29 09:00:22 -0400475 for obj in self.res['objects']:
476 client = client_for_user(obj['owner'])
477 r, contents = client.objects.get_object(
478 obj['container'], obj['name'])
479 source = _file_contents(obj['file'])
480 self.assertEqual(contents, source)
481
482 def check_servers(self):
483 """Check that the servers are still up and running."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000484 if not self.res.get('servers'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000485 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200486 LOG.info("checking servers")
Sean Dague655e0af2014-05-29 09:00:22 -0400487 for server in self.res['servers']:
488 client = client_for_user(server['owner'])
489 found = _get_server_by_name(client, server['name'])
490 self.assertIsNotNone(
491 found,
492 "Couldn't find expected server %s" % server['name'])
493
ghanshyam0f825252015-08-25 16:02:50 +0900494 found = client.servers.show_server(found['id'])['server']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200495 # validate neutron is enabled and ironic disabled:
496 if (CONF.service_available.neutron and
497 not CONF.baremetal.driver_enabled):
Emilien Macchic3e3e292015-03-11 17:42:08 -0400498 _floating_is_alive = False
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200499 for network_name, body in found['addresses'].items():
500 for addr in body:
501 ip = addr['addr']
lanoux283273b2015-12-04 03:01:54 -0800502 # Use floating IP, fixed IP or other type to
503 # reach the server.
Emilien Macchic3e3e292015-03-11 17:42:08 -0400504 # This is useful in multi-node environment.
lanoux283273b2015-12-04 03:01:54 -0800505 if CONF.validation.connect_method == 'floating':
Emilien Macchic3e3e292015-03-11 17:42:08 -0400506 if addr.get('OS-EXT-IPS:type',
507 'floating') == 'floating':
508 self._ping_ip(ip, 60)
509 _floating_is_alive = True
lanoux283273b2015-12-04 03:01:54 -0800510 elif CONF.validation.connect_method == 'fixed':
511 if addr.get('OS-EXT-IPS:type',
512 'fixed') == 'fixed':
513 namespace = _get_router_namespace(client,
514 network_name)
515 self._ping_ip(ip, 60, namespace)
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200516 else:
517 self._ping_ip(ip, 60)
lanoux283273b2015-12-04 03:01:54 -0800518 # If CONF.validation.connect_method is floating, validate
519 # that the floating IP is attached to the server and the
520 # the server is pingable.
521 if CONF.validation.connect_method == 'floating':
Emilien Macchic3e3e292015-03-11 17:42:08 -0400522 self.assertTrue(_floating_is_alive,
523 "Server %s has no floating IP." %
524 server['name'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200525 else:
526 addr = found['addresses']['private'][0]['addr']
527 self._ping_ip(addr, 60)
528
529 def check_secgroups(self):
Joe H. Rahme02736732015-01-27 18:33:09 +0100530 """Check that the security groups still exist."""
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200531 LOG.info("Checking security groups")
532 for secgroup in self.res['secgroups']:
533 client = client_for_user(secgroup['owner'])
534 found = _get_resource_by_name(client.secgroups, 'security_groups',
535 secgroup['name'])
536 self.assertIsNotNone(
537 found,
538 "Couldn't find expected secgroup %s" % secgroup['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400539
Chris Dent878f3782014-06-30 17:04:15 +0100540 def check_telemetry(self):
541 """Check that ceilometer provides a sane sample.
542
nayna-patel1dfbedb2015-08-04 11:07:56 +0000543 Confirm that there is more than one sample and that they have the
Chris Dent878f3782014-06-30 17:04:15 +0100544 expected metadata.
545
546 If in check mode confirm that the oldest sample available is from
547 before the upgrade.
548 """
Chris Dent0b76f2f2014-10-10 14:24:28 +0100549 if not self.res.get('telemetry'):
550 return
Chris Dent878f3782014-06-30 17:04:15 +0100551 LOG.info("checking telemetry")
552 for server in self.res['servers']:
553 client = client_for_user(server['owner'])
David Kranz20d06f42015-02-09 14:54:15 -0500554 body = client.telemetry.list_samples(
Chris Dent878f3782014-06-30 17:04:15 +0100555 'instance',
556 query=('metadata.display_name', 'eq', server['name'])
557 )
Chris Dent878f3782014-06-30 17:04:15 +0100558 self.assertTrue(len(body) >= 1, 'expecting at least one sample')
559 self._confirm_telemetry_sample(server, body[-1])
560
Emilien Macchi626b4f82014-06-15 21:44:29 +0200561 def check_volumes(self):
562 """Check that the volumes are still there and attached."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000563 if not self.res.get('volumes'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000564 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200565 LOG.info("checking volumes")
Emilien Macchi626b4f82014-06-15 21:44:29 +0200566 for volume in self.res['volumes']:
567 client = client_for_user(volume['owner'])
Emilien Macchid18fec12014-09-15 14:32:54 -0400568 vol_body = _get_volume_by_name(client, volume['name'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200569 self.assertIsNotNone(
Emilien Macchid18fec12014-09-15 14:32:54 -0400570 vol_body,
Emilien Macchi626b4f82014-06-15 21:44:29 +0200571 "Couldn't find expected volume %s" % volume['name'])
572
573 # Verify that a volume's attachment retrieved
574 server_id = _get_server_by_name(client, volume['server'])['id']
Emilien Macchid18fec12014-09-15 14:32:54 -0400575 attachment = client.volumes.get_attachment_from_volume(vol_body)
576 self.assertEqual(vol_body['id'], attachment['volume_id'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200577 self.assertEqual(server_id, attachment['server_id'])
578
Chris Dent878f3782014-06-30 17:04:15 +0100579 def _confirm_telemetry_sample(self, server, sample):
580 """Check this sample matches the expected resource metadata."""
581 # Confirm display_name
582 self.assertEqual(server['name'],
583 sample['resource_metadata']['display_name'])
584 # Confirm instance_type of flavor
585 flavor = sample['resource_metadata'].get(
586 'flavor.name',
587 sample['resource_metadata'].get('instance_type')
588 )
589 self.assertEqual(server['flavor'], flavor)
590 # Confirm the oldest sample was created before upgrade.
591 if OPTS.mode == 'check':
592 oldest_timestamp = timeutils.normalize_time(
593 timeutils.parse_isotime(sample['timestamp']))
594 self.assertTrue(
595 oldest_timestamp < JAVELIN_START,
596 'timestamp should come before start of second javelin run'
597 )
598
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200599 def check_networking(self):
600 """Check that the networks are still there."""
601 for res_type in ('networks', 'subnets', 'routers'):
602 for res in self.res[res_type]:
603 client = client_for_user(res['owner'])
604 found = _get_resource_by_name(client.networks, res_type,
605 res['name'])
606 self.assertIsNotNone(
607 found,
608 "Couldn't find expected resource %s" % res['name'])
609
Sean Dague655e0af2014-05-29 09:00:22 -0400610
611#######################
612#
613# OBJECTS
614#
615#######################
616
617
618def _file_contents(fname):
619 with open(fname, 'r') as f:
620 return f.read()
621
622
623def create_objects(objects):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000624 if not objects:
625 return
Sean Dague655e0af2014-05-29 09:00:22 -0400626 LOG.info("Creating objects")
627 for obj in objects:
628 LOG.debug("Object %s" % obj)
Joe H. Rahme9c9fc4d2015-03-31 00:35:05 +0200629 swift_role = obj.get('swift_role', 'Member')
630 _assign_swift_role(obj['owner'], swift_role)
Sean Dague655e0af2014-05-29 09:00:22 -0400631 client = client_for_user(obj['owner'])
632 client.containers.create_container(obj['container'])
633 client.objects.create_object(
634 obj['container'], obj['name'],
635 _file_contents(obj['file']))
636
Emilien Macchibb71e072014-07-05 19:18:52 +0200637
638def destroy_objects(objects):
639 for obj in objects:
640 client = client_for_user(obj['owner'])
641 r, body = client.objects.delete_object(obj['container'], obj['name'])
Emilien Macchid70f5102014-09-10 09:54:49 -0400642 if not (200 <= int(r['status']) < 299):
Emilien Macchibb71e072014-07-05 19:18:52 +0200643 raise ValueError("unable to destroy object: [%s] %s" % (r, body))
644
645
Sean Dague655e0af2014-05-29 09:00:22 -0400646#######################
647#
648# IMAGES
649#
650#######################
651
652
Sean Dague319b37a2014-07-11 07:28:11 -0400653def _resolve_image(image, imgtype):
654 name = image[imgtype]
655 fname = os.path.join(OPTS.devstack_base, image['imgdir'], name)
656 return name, fname
657
658
Joe Gordon6f0426c2014-07-25 01:10:28 +0000659def _get_image_by_name(client, name):
Ken'ichi Ohmichie3acc122015-05-22 00:32:54 +0000660 body = client.images.list_images()
Joe Gordon6f0426c2014-07-25 01:10:28 +0000661 for image in body:
662 if name == image['name']:
663 return image
664 return None
665
666
Sean Dague655e0af2014-05-29 09:00:22 -0400667def create_images(images):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000668 if not images:
669 return
Joe Gordona18d6862014-07-24 22:55:46 +0000670 LOG.info("Creating images")
Sean Dague655e0af2014-05-29 09:00:22 -0400671 for image in images:
672 client = client_for_user(image['owner'])
673
Joe H. Rahme4352d5d2015-03-09 17:41:18 +0100674 # DEPRECATED: 'format' was used for ami images
675 # Use 'disk_format' and 'container_format' instead
676 if 'format' in image:
677 LOG.warning("Deprecated: 'format' is deprecated for images "
678 "description. Please use 'disk_format' and 'container_"
679 "format' instead.")
680 image['disk_format'] = image['format']
681 image['container_format'] = image['format']
682
Sean Dague655e0af2014-05-29 09:00:22 -0400683 # only upload a new image if the name isn't there
Joe Gordon6f0426c2014-07-25 01:10:28 +0000684 if _get_image_by_name(client, image['name']):
Joe Gordona18d6862014-07-24 22:55:46 +0000685 LOG.info("Image '%s' already exists" % image['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400686 continue
687
688 # special handling for 3 part image
689 extras = {}
Joe H. Rahme4352d5d2015-03-09 17:41:18 +0100690 if image['disk_format'] == 'ami':
Sean Dague319b37a2014-07-11 07:28:11 -0400691 name, fname = _resolve_image(image, 'aki')
David Kranz34f18782015-01-06 13:43:55 -0500692 aki = client.images.create_image(
Sean Dague319b37a2014-07-11 07:28:11 -0400693 'javelin_' + name, 'aki', 'aki')
Ken'ichi Ohmichi66494e92015-06-08 04:28:10 +0000694 client.images.store_image_file(aki.get('id'), open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400695 extras['kernel_id'] = aki.get('id')
696
Sean Dague319b37a2014-07-11 07:28:11 -0400697 name, fname = _resolve_image(image, 'ari')
David Kranz34f18782015-01-06 13:43:55 -0500698 ari = client.images.create_image(
Sean Dague319b37a2014-07-11 07:28:11 -0400699 'javelin_' + name, 'ari', 'ari')
Ken'ichi Ohmichi66494e92015-06-08 04:28:10 +0000700 client.images.store_image_file(ari.get('id'), open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400701 extras['ramdisk_id'] = ari.get('id')
702
Sean Dague319b37a2014-07-11 07:28:11 -0400703 _, fname = _resolve_image(image, 'file')
David Kranz34f18782015-01-06 13:43:55 -0500704 body = client.images.create_image(
Joe H. Rahme4352d5d2015-03-09 17:41:18 +0100705 image['name'], image['container_format'],
706 image['disk_format'], **extras)
Sean Dague655e0af2014-05-29 09:00:22 -0400707 image_id = body.get('id')
Ken'ichi Ohmichi66494e92015-06-08 04:28:10 +0000708 client.images.store_image_file(image_id, open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400709
710
Joe Gordon6f0426c2014-07-25 01:10:28 +0000711def destroy_images(images):
712 if not images:
713 return
714 LOG.info("Destroying images")
715 for image in images:
716 client = client_for_user(image['owner'])
717
718 response = _get_image_by_name(client, image['name'])
719 if not response:
nayna-patel1dfbedb2015-08-04 11:07:56 +0000720 LOG.info("Image '%s' does not exist" % image['name'])
Joe Gordon6f0426c2014-07-25 01:10:28 +0000721 continue
722 client.images.delete_image(response['id'])
723
724
Sean Dague655e0af2014-05-29 09:00:22 -0400725#######################
726#
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200727# NETWORKS
728#
729#######################
730
731def _get_router_namespace(client, network):
732 network_id = _get_resource_by_name(client.networks,
733 'networks', network)['id']
David Kranz34e88122014-12-11 15:24:05 -0500734 n_body = client.networks.list_routers()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200735 for router in n_body['routers']:
736 router_id = router['id']
David Kranz34e88122014-12-11 15:24:05 -0500737 r_body = client.networks.list_router_interfaces(router_id)
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200738 for port in r_body['ports']:
739 if port['network_id'] == network_id:
740 return "qrouter-%s" % router_id
741
742
743def _get_resource_by_name(client, resource, name):
744 get_resources = getattr(client, 'list_%s' % resource)
745 if get_resources is None:
746 raise AttributeError("client doesn't have method list_%s" % resource)
David Kranz34e88122014-12-11 15:24:05 -0500747 # Until all tempest client methods are changed to return only one value,
748 # we cannot assume they all have the same signature so we need to discard
749 # the unused response first value it two values are being returned.
750 body = get_resources()
751 if type(body) == tuple:
752 body = body[1]
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200753 if isinstance(body, dict):
754 body = body[resource]
755 for res in body:
756 if name == res['name']:
757 return res
758 raise ValueError('%s not found in %s resources' % (name, resource))
759
760
761def create_networks(networks):
762 LOG.info("Creating networks")
763 for network in networks:
764 client = client_for_user(network['owner'])
765
766 # only create a network if the name isn't here
David Kranz34e88122014-12-11 15:24:05 -0500767 body = client.networks.list_networks()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200768 if any(item['name'] == network['name'] for item in body['networks']):
nayna-patel1dfbedb2015-08-04 11:07:56 +0000769 LOG.warning("Duplicated network name: %s" % network['name'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200770 continue
771
772 client.networks.create_network(name=network['name'])
773
774
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100775def destroy_networks(networks):
776 LOG.info("Destroying subnets")
777 for network in networks:
778 client = client_for_user(network['owner'])
779 network_id = _get_resource_by_name(client.networks, 'networks',
780 network['name'])['id']
781 client.networks.delete_network(network_id)
782
783
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200784def create_subnets(subnets):
785 LOG.info("Creating subnets")
786 for subnet in subnets:
787 client = client_for_user(subnet['owner'])
788
789 network = _get_resource_by_name(client.networks, 'networks',
790 subnet['network'])
791 ip_version = netaddr.IPNetwork(subnet['range']).version
792 # ensure we don't overlap with another subnet in the network
793 try:
794 client.networks.create_subnet(network_id=network['id'],
795 cidr=subnet['range'],
796 name=subnet['name'],
797 ip_version=ip_version)
Masayuki Igawa4b29e472015-02-16 10:41:54 +0900798 except lib_exc.BadRequest as e:
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200799 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
800 if not is_overlapping_cidr:
801 raise
802
803
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100804def destroy_subnets(subnets):
805 LOG.info("Destroying subnets")
806 for subnet in subnets:
807 client = client_for_user(subnet['owner'])
John Warren3961acd2015-10-02 14:38:53 -0400808 subnet_id = _get_resource_by_name(client.subnets,
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100809 'subnets', subnet['name'])['id']
John Warren3961acd2015-10-02 14:38:53 -0400810 client.subnets.delete_subnet(subnet_id)
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100811
812
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200813def create_routers(routers):
814 LOG.info("Creating routers")
815 for router in routers:
816 client = client_for_user(router['owner'])
817
818 # only create a router if the name isn't here
David Kranz34e88122014-12-11 15:24:05 -0500819 body = client.networks.list_routers()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200820 if any(item['name'] == router['name'] for item in body['routers']):
nayna-patel1dfbedb2015-08-04 11:07:56 +0000821 LOG.warning("Duplicated router name: %s" % router['name'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200822 continue
823
824 client.networks.create_router(router['name'])
825
826
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100827def destroy_routers(routers):
828 LOG.info("Destroying routers")
829 for router in routers:
830 client = client_for_user(router['owner'])
831 router_id = _get_resource_by_name(client.networks,
832 'routers', router['name'])['id']
833 for subnet in router['subnet']:
834 subnet_id = _get_resource_by_name(client.networks,
835 'subnets', subnet)['id']
836 client.networks.remove_router_interface_with_subnet_id(router_id,
837 subnet_id)
838 client.networks.delete_router(router_id)
839
840
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200841def add_router_interface(routers):
842 for router in routers:
843 client = client_for_user(router['owner'])
844 router_id = _get_resource_by_name(client.networks,
845 'routers', router['name'])['id']
846
847 for subnet in router['subnet']:
848 subnet_id = _get_resource_by_name(client.networks,
849 'subnets', subnet)['id']
850 # connect routers to their subnets
851 client.networks.add_router_interface_with_subnet_id(router_id,
852 subnet_id)
nayna-patel1dfbedb2015-08-04 11:07:56 +0000853 # connect routers to external network if set to "gateway"
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200854 if router['gateway']:
855 if CONF.network.public_network_id:
856 ext_net = CONF.network.public_network_id
857 client.networks._update_router(
858 router_id, set_enable_snat=True,
859 external_gateway_info={"network_id": ext_net})
860 else:
861 raise ValueError('public_network_id is not configured.')
862
863
864#######################
865#
Sean Dague655e0af2014-05-29 09:00:22 -0400866# SERVERS
867#
868#######################
869
870def _get_server_by_name(client, name):
David Kranzae99b9a2015-02-16 13:37:01 -0500871 body = client.servers.list_servers()
Sean Dague655e0af2014-05-29 09:00:22 -0400872 for server in body['servers']:
873 if name == server['name']:
874 return server
875 return None
876
877
Sean Dague655e0af2014-05-29 09:00:22 -0400878def _get_flavor_by_name(client, name):
ghanshyam19973be2015-08-18 15:46:42 +0900879 body = client.flavors.list_flavors()['flavors']
Sean Dague655e0af2014-05-29 09:00:22 -0400880 for flavor in body:
881 if name == flavor['name']:
882 return flavor
883 return None
884
885
886def create_servers(servers):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000887 if not servers:
888 return
Joe Gordona18d6862014-07-24 22:55:46 +0000889 LOG.info("Creating servers")
Sean Dague655e0af2014-05-29 09:00:22 -0400890 for server in servers:
891 client = client_for_user(server['owner'])
892
893 if _get_server_by_name(client, server['name']):
Joe Gordona18d6862014-07-24 22:55:46 +0000894 LOG.info("Server '%s' already exists" % server['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400895 continue
896
897 image_id = _get_image_by_name(client, server['image'])['id']
898 flavor_id = _get_flavor_by_name(client, server['flavor'])['id']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200899 # validate neutron is enabled and ironic disabled
900 kwargs = dict()
901 if (CONF.service_available.neutron and
902 not CONF.baremetal.driver_enabled and server.get('networks')):
903 get_net_id = lambda x: (_get_resource_by_name(
904 client.networks, 'networks', x)['id'])
905 kwargs['networks'] = [{'uuid': get_net_id(network)}
906 for network in server['networks']]
David Kranz0fb14292015-02-11 15:55:20 -0500907 body = client.servers.create_server(
Ken'ichi Ohmichif2d436e2015-09-03 01:13:16 +0000908 name=server['name'], imageRef=image_id, flavorRef=flavor_id,
909 **kwargs)['server']
Joe Gordon10f260b2014-07-24 23:27:19 +0000910 server_id = body['id']
911 client.servers.wait_for_server_status(server_id, 'ACTIVE')
nayna-patel1dfbedb2015-08-04 11:07:56 +0000912 # create security group(s) after server spawning
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200913 for secgroup in server['secgroups']:
Ken'ichi Ohmichie6349f32015-12-09 06:47:54 +0000914 client.servers.add_security_group(server_id, name=secgroup)
lanoux283273b2015-12-04 03:01:54 -0800915 if CONF.validation.connect_method == 'floating':
Joe H. Rahme9350a102015-03-29 17:39:20 +0200916 floating_ip_pool = server.get('floating_ip_pool')
917 floating_ip = client.floating_ips.create_floating_ip(
ghanshyam9a3a9a22015-08-18 17:03:55 +0900918 pool_name=floating_ip_pool)['floating_ip']
Emilien Macchic3e3e292015-03-11 17:42:08 -0400919 client.floating_ips.associate_floating_ip_to_server(
920 floating_ip['ip'], server_id)
Sean Dague655e0af2014-05-29 09:00:22 -0400921
922
Joe Gordondb63b1c2014-07-24 23:21:21 +0000923def destroy_servers(servers):
924 if not servers:
925 return
926 LOG.info("Destroying servers")
927 for server in servers:
928 client = client_for_user(server['owner'])
929
Emilien Macchidc5bae22015-03-16 08:49:02 -0400930 response = _get_server_by_name(client, server['name'])
931 if not response:
Joe Gordondb63b1c2014-07-24 23:21:21 +0000932 LOG.info("Server '%s' does not exist" % server['name'])
933 continue
934
Emilien Macchidc5bae22015-03-16 08:49:02 -0400935 # TODO(EmilienM): disassociate floating IP from server and release it.
936 client.servers.delete_server(response['id'])
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000937 waiters.wait_for_server_termination(client.servers, response['id'],
938 ignore_error=True)
Joe Gordondb63b1c2014-07-24 23:21:21 +0000939
940
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200941def create_secgroups(secgroups):
942 LOG.info("Creating security groups")
943 for secgroup in secgroups:
944 client = client_for_user(secgroup['owner'])
945
946 # only create a security group if the name isn't here
947 # i.e. a security group may be used by another server
948 # only create a router if the name isn't here
ghanshyamb610b772015-08-24 17:29:38 +0900949 body = client.secgroups.list_security_groups()['security_groups']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200950 if any(item['name'] == secgroup['name'] for item in body):
951 LOG.warning("Security group '%s' already exists" %
952 secgroup['name'])
953 continue
954
David Kranz9964b4e2015-02-06 15:45:29 -0500955 body = client.secgroups.create_security_group(
ghanshyamb610b772015-08-24 17:29:38 +0900956 name=secgroup['name'],
957 description=secgroup['description'])['security_group']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200958 secgroup_id = body['id']
959 # for each security group, create the rules
960 for rule in secgroup['rules']:
961 ip_proto, from_port, to_port, cidr = rule.split()
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +0000962 client.secrules.create_security_group_rule(
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000963 parent_group_id=secgroup_id, ip_protocol=ip_proto,
964 from_port=from_port, to_port=to_port, cidr=cidr)
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200965
966
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100967def destroy_secgroups(secgroups):
968 LOG.info("Destroying security groups")
969 for secgroup in secgroups:
970 client = client_for_user(secgroup['owner'])
971 sg_id = _get_resource_by_name(client.secgroups,
972 'security_groups',
973 secgroup['name'])
974 # sg rules are deleted automatically
975 client.secgroups.delete_security_group(sg_id['id'])
976
977
Sean Dague655e0af2014-05-29 09:00:22 -0400978#######################
979#
Emilien Macchi626b4f82014-06-15 21:44:29 +0200980# VOLUMES
981#
982#######################
983
984def _get_volume_by_name(client, name):
John Warren6177c9e2015-08-19 20:00:17 +0000985 body = client.volumes.list_volumes()['volumes']
Emilien Macchid18fec12014-09-15 14:32:54 -0400986 for volume in body:
987 if name == volume['display_name']:
Emilien Macchi626b4f82014-06-15 21:44:29 +0200988 return volume
989 return None
990
991
992def create_volumes(volumes):
Chris Dent51e76de2014-10-01 12:07:14 +0100993 if not volumes:
994 return
995 LOG.info("Creating volumes")
Emilien Macchi626b4f82014-06-15 21:44:29 +0200996 for volume in volumes:
997 client = client_for_user(volume['owner'])
998
999 # only create a volume if the name isn't here
Emilien Macchid18fec12014-09-15 14:32:54 -04001000 if _get_volume_by_name(client, volume['name']):
1001 LOG.info("volume '%s' already exists" % volume['name'])
Emilien Macchi626b4f82014-06-15 21:44:29 +02001002 continue
1003
Emilien Macchid18fec12014-09-15 14:32:54 -04001004 size = volume['gb']
1005 v_name = volume['name']
Joseph Lanoux6809bab2014-12-18 14:57:18 +00001006 body = client.volumes.create_volume(size=size,
John Warren6177c9e2015-08-19 20:00:17 +00001007 display_name=v_name)['volume']
Emilien Macchid18fec12014-09-15 14:32:54 -04001008 client.volumes.wait_for_volume_status(body['id'], 'available')
Emilien Macchi626b4f82014-06-15 21:44:29 +02001009
1010
Emilien Macchibb71e072014-07-05 19:18:52 +02001011def destroy_volumes(volumes):
1012 for volume in volumes:
1013 client = client_for_user(volume['owner'])
1014 volume_id = _get_volume_by_name(client, volume['name'])['id']
Emilien Macchi5ebc27b2014-09-15 14:30:35 -04001015 client.volumes.detach_volume(volume_id)
1016 client.volumes.delete_volume(volume_id)
Emilien Macchibb71e072014-07-05 19:18:52 +02001017
1018
Emilien Macchi626b4f82014-06-15 21:44:29 +02001019def attach_volumes(volumes):
1020 for volume in volumes:
1021 client = client_for_user(volume['owner'])
Emilien Macchi626b4f82014-06-15 21:44:29 +02001022 server_id = _get_server_by_name(client, volume['server'])['id']
Emilien Macchid18fec12014-09-15 14:32:54 -04001023 volume_id = _get_volume_by_name(client, volume['name'])['id']
1024 device = volume['device']
1025 client.volumes.attach_volume(volume_id, server_id, device)
Emilien Macchi626b4f82014-06-15 21:44:29 +02001026
1027
1028#######################
1029#
Sean Dague655e0af2014-05-29 09:00:22 -04001030# MAIN LOGIC
1031#
1032#######################
1033
1034def create_resources():
1035 LOG.info("Creating Resources")
1036 # first create keystone level resources, and we need to be admin
nayna-patel1dfbedb2015-08-04 11:07:56 +00001037 # for this.
Sean Dague655e0af2014-05-29 09:00:22 -04001038 create_tenants(RES['tenants'])
1039 create_users(RES['users'])
1040 collect_users(RES['users'])
1041
1042 # next create resources in a well known order
1043 create_objects(RES['objects'])
1044 create_images(RES['images'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +02001045
1046 # validate neutron is enabled and ironic is disabled
1047 if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
1048 create_networks(RES['networks'])
1049 create_subnets(RES['subnets'])
1050 create_routers(RES['routers'])
1051 add_router_interface(RES['routers'])
1052
1053 create_secgroups(RES['secgroups'])
Emilien Macchid18fec12014-09-15 14:32:54 -04001054 create_volumes(RES['volumes'])
Joe H. Rahmec96129b2015-03-30 11:23:31 +02001055
1056 # Only attempt attaching the volumes if servers are defined in the
nayna-patel1dfbedb2015-08-04 11:07:56 +00001057 # resource file
Joe H. Rahmec96129b2015-03-30 11:23:31 +02001058 if 'servers' in RES:
1059 create_servers(RES['servers'])
1060 attach_volumes(RES['volumes'])
Sean Dague655e0af2014-05-29 09:00:22 -04001061
1062
Joe Gordondb63b1c2014-07-24 23:21:21 +00001063def destroy_resources():
1064 LOG.info("Destroying Resources")
1065 # Destroy in inverse order of create
Joe Gordondb63b1c2014-07-24 23:21:21 +00001066 destroy_servers(RES['servers'])
Joe Gordon6f0426c2014-07-25 01:10:28 +00001067 destroy_images(RES['images'])
Emilien Macchibb71e072014-07-05 19:18:52 +02001068 destroy_objects(RES['objects'])
Emilien Macchibb71e072014-07-05 19:18:52 +02001069 destroy_volumes(RES['volumes'])
Jakub Libosvar3791ac92014-11-11 13:23:44 +01001070 if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
1071 destroy_routers(RES['routers'])
1072 destroy_subnets(RES['subnets'])
1073 destroy_networks(RES['networks'])
1074 destroy_secgroups(RES['secgroups'])
Emilien Macchibb71e072014-07-05 19:18:52 +02001075 destroy_users(RES['users'])
1076 destroy_tenants(RES['tenants'])
Joe Gordon6f0426c2014-07-25 01:10:28 +00001077 LOG.warn("Destroy mode incomplete")
1078
Joe Gordondb63b1c2014-07-24 23:21:21 +00001079
Sean Dague655e0af2014-05-29 09:00:22 -04001080def get_options():
1081 global OPTS
1082 parser = argparse.ArgumentParser(
1083 description='Create and validate a fixed set of OpenStack resources')
1084 parser.add_argument('-m', '--mode',
1085 metavar='<create|check|destroy>',
1086 required=True,
1087 help=('One of (create, check, destroy)'))
1088 parser.add_argument('-r', '--resources',
1089 required=True,
1090 metavar='resourcefile.yaml',
1091 help='Resources definition yaml file')
Joe Gordon28a84ae2014-07-17 15:38:28 +00001092
Sean Dague319b37a2014-07-11 07:28:11 -04001093 parser.add_argument(
1094 '-d', '--devstack-base',
1095 required=True,
1096 metavar='/opt/stack/old',
1097 help='Devstack base directory for retrieving artifacts')
Joe Gordon28a84ae2014-07-17 15:38:28 +00001098 parser.add_argument(
1099 '-c', '--config-file',
1100 metavar='/etc/tempest.conf',
1101 help='path to javelin2(tempest) config file')
1102
Sean Dague655e0af2014-05-29 09:00:22 -04001103 # auth bits, letting us also just source the devstack openrc
1104 parser.add_argument('--os-username',
1105 metavar='<auth-user-name>',
1106 default=os.environ.get('OS_USERNAME'),
1107 help=('Defaults to env[OS_USERNAME].'))
1108 parser.add_argument('--os-password',
1109 metavar='<auth-password>',
1110 default=os.environ.get('OS_PASSWORD'),
1111 help=('Defaults to env[OS_PASSWORD].'))
1112 parser.add_argument('--os-tenant-name',
1113 metavar='<auth-tenant-name>',
1114 default=os.environ.get('OS_TENANT_NAME'),
1115 help=('Defaults to env[OS_TENANT_NAME].'))
1116
1117 OPTS = parser.parse_args()
1118 if OPTS.mode not in ('create', 'check', 'destroy'):
1119 print("ERROR: Unknown mode -m %s\n" % OPTS.mode)
1120 parser.print_help()
1121 sys.exit(1)
Joe Gordon28a84ae2014-07-17 15:38:28 +00001122 if OPTS.config_file:
1123 config.CONF.set_config_path(OPTS.config_file)
Sean Dague655e0af2014-05-29 09:00:22 -04001124
1125
Joe Gordon915eb8e2014-07-17 11:25:46 +02001126def setup_logging():
Sean Dague655e0af2014-05-29 09:00:22 -04001127 global LOG
Doug Hellmann583ce2c2015-03-11 14:55:46 +00001128 logging.setup(CONF, __name__)
Sean Dague655e0af2014-05-29 09:00:22 -04001129 LOG = logging.getLogger(__name__)
Sean Dague655e0af2014-05-29 09:00:22 -04001130
1131
1132def main():
1133 global RES
1134 get_options()
1135 setup_logging()
Chris Dent51e76de2014-10-01 12:07:14 +01001136 RES.update(load_resources(OPTS.resources))
Sean Dague655e0af2014-05-29 09:00:22 -04001137
1138 if OPTS.mode == 'create':
1139 create_resources()
Joe Gordon1a097002014-07-24 23:44:08 +00001140 # Make sure the resources we just created actually work
1141 checker = JavelinCheck(USERS, RES)
1142 checker.check()
Sean Dague655e0af2014-05-29 09:00:22 -04001143 elif OPTS.mode == 'check':
1144 collect_users(RES['users'])
1145 checker = JavelinCheck(USERS, RES)
1146 checker.check()
1147 elif OPTS.mode == 'destroy':
Joe Gordondb63b1c2014-07-24 23:21:21 +00001148 collect_users(RES['users'])
1149 destroy_resources()
Sean Dague655e0af2014-05-29 09:00:22 -04001150 else:
1151 LOG.error('Unknown mode %s' % OPTS.mode)
1152 return 1
Joe Gordon246353a2014-07-18 00:10:28 +02001153 LOG.info('javelin2 successfully finished')
Sean Dague655e0af2014-05-29 09:00:22 -04001154 return 0
1155
1156if __name__ == "__main__":
1157 sys.exit(main())