blob: 184bb9ae0e08e7c3f6520781b6167f033ef41427 [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
Sean Dague655e0af2014-05-29 09:00:22 -0400131from tempest.services.image.v2.json import image_client
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200132from tempest.services.network.json import network_client
John Warren3961acd2015-10-02 14:38:53 -0400133from tempest.services.network.json import subnets_client
Sean Dague655e0af2014-05-29 09:00:22 -0400134from tempest.services.object_storage import container_client
135from tempest.services.object_storage import object_client
liu-sheng67b730e2015-07-16 15:19:33 +0800136from tempest.services.telemetry.json import alarming_client
Chris Dent878f3782014-06-30 17:04:15 +0100137from tempest.services.telemetry.json import telemetry_client
Emilien Macchi626b4f82014-06-15 21:44:29 +0200138from tempest.services.volume.json import volumes_client
Sean Dague655e0af2014-05-29 09:00:22 -0400139
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200140CONF = config.CONF
Sean Dague655e0af2014-05-29 09:00:22 -0400141OPTS = {}
142USERS = {}
Chris Dent51e76de2014-10-01 12:07:14 +0100143RES = collections.defaultdict(list)
Sean Dague655e0af2014-05-29 09:00:22 -0400144
145LOG = None
146
Chris Dent878f3782014-06-30 17:04:15 +0100147JAVELIN_START = datetime.datetime.utcnow()
148
Sean Dague655e0af2014-05-29 09:00:22 -0400149
150class OSClient(object):
151 _creds = None
152 identity = None
153 servers = None
154
155 def __init__(self, user, pw, tenant):
Ken'ichi Ohmichi4771cbc2015-01-19 23:45:23 +0000156 default_params = {
157 'disable_ssl_certificate_validation':
158 CONF.identity.disable_ssl_certificate_validation,
159 'ca_certs': CONF.identity.ca_certificates_file,
160 'trace_requests': CONF.debug.trace_requests
161 }
Ken'ichi Ohmichid5dba1c2015-01-23 02:23:22 +0000162 default_params_with_timeout_values = {
163 'build_interval': CONF.compute.build_interval,
164 'build_timeout': CONF.compute.build_timeout
165 }
166 default_params_with_timeout_values.update(default_params)
167
Ken'ichi Ohmichi4771cbc2015-01-19 23:45:23 +0000168 compute_params = {
169 'service': CONF.compute.catalog_type,
170 'region': CONF.compute.region or CONF.identity.region,
171 'endpoint_type': CONF.compute.endpoint_type,
172 'build_interval': CONF.compute.build_interval,
173 'build_timeout': CONF.compute.build_timeout
174 }
175 compute_params.update(default_params)
176
Ken'ichi Ohmichi564b2ad2015-01-22 02:08:59 +0000177 object_storage_params = {
178 'service': CONF.object_storage.catalog_type,
179 'region': CONF.object_storage.region or CONF.identity.region,
180 'endpoint_type': CONF.object_storage.endpoint_type
181 }
182 object_storage_params.update(default_params)
183
andreafb8a52282015-03-19 22:21:54 +0000184 _creds = auth.KeystoneV2Credentials(
Sean Dague655e0af2014-05-29 09:00:22 -0400185 username=user,
186 password=pw,
187 tenant_name=tenant)
Andrea Frittoli90012352015-02-25 21:58:02 +0000188 auth_provider_params = {
189 'disable_ssl_certificate_validation':
190 CONF.identity.disable_ssl_certificate_validation,
191 'ca_certs': CONF.identity.ca_certificates_file,
192 'trace_requests': CONF.debug.trace_requests
193 }
andreafb8a52282015-03-19 22:21:54 +0000194 _auth = auth.KeystoneV2AuthProvider(
Andrea Frittoli90012352015-02-25 21:58:02 +0000195 _creds, CONF.identity.uri, **auth_provider_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000196 self.identity = identity_client.IdentityClient(
ghanshyamd26b5cd2015-02-09 14:48:58 +0900197 _auth,
198 CONF.identity.catalog_type,
199 CONF.identity.region,
200 endpoint_type='adminURL',
201 **default_params_with_timeout_values)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000202 self.servers = servers_client.ServersClient(_auth,
203 **compute_params)
204 self.flavors = flavors_client.FlavorsClient(_auth,
205 **compute_params)
206 self.floating_ips = floating_ips_client.FloatingIPsClient(
Emilien Macchic3e3e292015-03-11 17:42:08 -0400207 _auth, **compute_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000208 self.secgroups = security_groups_client.SecurityGroupsClient(
Ken'ichi Ohmichi4771cbc2015-01-19 23:45:23 +0000209 _auth, **compute_params)
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +0000210 self.secrules = security_group_rules_client.SecurityGroupRulesClient(
211 _auth, **compute_params)
Ken'ichi Ohmichi564b2ad2015-01-22 02:08:59 +0000212 self.objects = object_client.ObjectClient(_auth,
213 **object_storage_params)
214 self.containers = container_client.ContainerClient(
215 _auth, **object_storage_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000216 self.images = image_client.ImageClientV2(
Masayuki Igawabc7e1892015-03-03 11:46:48 +0900217 _auth,
218 CONF.image.catalog_type,
219 CONF.image.region or CONF.identity.region,
220 endpoint_type=CONF.image.endpoint_type,
221 build_interval=CONF.image.build_interval,
222 build_timeout=CONF.image.build_timeout,
223 **default_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000224 self.telemetry = telemetry_client.TelemetryClient(
Ken'ichi Ohmichid5dba1c2015-01-23 02:23:22 +0000225 _auth,
226 CONF.telemetry.catalog_type,
227 CONF.identity.region,
228 endpoint_type=CONF.telemetry.endpoint_type,
229 **default_params_with_timeout_values)
liu-sheng67b730e2015-07-16 15:19:33 +0800230 self.alarming = alarming_client.AlarmingClient(
231 _auth,
232 CONF.alarm.catalog_type,
233 CONF.identity.region,
234 endpoint_type=CONF.alarm.endpoint_type,
235 **default_params_with_timeout_values)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000236 self.volumes = volumes_client.VolumesClient(
Ken'ichi Ohmichif85e9bd2015-01-27 12:51:47 +0000237 _auth,
238 CONF.volume.catalog_type,
239 CONF.volume.region or CONF.identity.region,
240 endpoint_type=CONF.volume.endpoint_type,
241 build_interval=CONF.volume.build_interval,
242 build_timeout=CONF.volume.build_timeout,
243 **default_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000244 self.networks = network_client.NetworkClient(
Ken'ichi Ohmichia182e862015-01-21 01:16:37 +0000245 _auth,
246 CONF.network.catalog_type,
247 CONF.network.region or CONF.identity.region,
248 endpoint_type=CONF.network.endpoint_type,
249 build_interval=CONF.network.build_interval,
250 build_timeout=CONF.network.build_timeout,
251 **default_params)
John Warren3961acd2015-10-02 14:38:53 -0400252 self.subnets = subnets_client.SubnetsClient(
253 _auth,
254 CONF.network.catalog_type,
255 CONF.network.region or CONF.identity.region,
256 endpoint_type=CONF.network.endpoint_type,
257 build_interval=CONF.network.build_interval,
258 build_timeout=CONF.network.build_timeout,
259 **default_params)
Sean Dague655e0af2014-05-29 09:00:22 -0400260
261
262def load_resources(fname):
Joe H. Rahme02736732015-01-27 18:33:09 +0100263 """Load the expected resources from a yaml file."""
Sean Dague655e0af2014-05-29 09:00:22 -0400264 return yaml.load(open(fname, 'r'))
265
266
267def keystone_admin():
268 return OSClient(OPTS.os_username, OPTS.os_password, OPTS.os_tenant_name)
269
270
271def client_for_user(name):
272 LOG.debug("Entering client_for_user")
273 if name in USERS:
274 user = USERS[name]
275 LOG.debug("Created client for user %s" % user)
276 return OSClient(user['name'], user['pass'], user['tenant'])
277 else:
278 LOG.error("%s not found in USERS: %s" % (name, USERS))
279
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200280
Sean Dague655e0af2014-05-29 09:00:22 -0400281###################
282#
283# TENANTS
284#
285###################
286
287
288def create_tenants(tenants):
289 """Create tenants from resource definition.
290
291 Don't create the tenants if they already exist.
292 """
293 admin = keystone_admin()
John Warrenacf00512015-08-06 16:13:58 -0400294 body = admin.identity.list_tenants()['tenants']
Sean Dague655e0af2014-05-29 09:00:22 -0400295 existing = [x['name'] for x in body]
296 for tenant in tenants:
297 if tenant not in existing:
Anusha Ramineni0cfb4612015-08-24 08:49:10 +0530298 admin.identity.create_tenant(tenant)['tenant']
Sean Dague655e0af2014-05-29 09:00:22 -0400299 else:
300 LOG.warn("Tenant '%s' already exists in this environment" % tenant)
301
Emilien Macchibb71e072014-07-05 19:18:52 +0200302
303def destroy_tenants(tenants):
304 admin = keystone_admin()
305 for tenant in tenants:
Ken'ichi Ohmichi6ea3f982015-11-09 12:41:13 +0000306 tenant_id = identity.get_tenant_by_name(admin.identity, tenant)['id']
David Kranzb7afa922014-12-30 10:56:26 -0500307 admin.identity.delete_tenant(tenant_id)
Emilien Macchibb71e072014-07-05 19:18:52 +0200308
Sean Dague655e0af2014-05-29 09:00:22 -0400309##############
310#
311# USERS
312#
313##############
314
315
316def _users_for_tenant(users, tenant):
317 u_for_t = []
318 for user in users:
319 for n in user:
320 if user[n]['tenant'] == tenant:
321 u_for_t.append(user[n])
322 return u_for_t
323
324
325def _tenants_from_users(users):
326 tenants = set()
327 for user in users:
328 for n in user:
329 tenants.add(user[n]['tenant'])
330 return tenants
331
332
Joe H. Rahme9c9fc4d2015-03-31 00:35:05 +0200333def _assign_swift_role(user, swift_role):
Sean Dague655e0af2014-05-29 09:00:22 -0400334 admin = keystone_admin()
David Kranzb7afa922014-12-30 10:56:26 -0500335 roles = admin.identity.list_roles()
Joe H. Rahme9c9fc4d2015-03-31 00:35:05 +0200336 role = next(r for r in roles if r['name'] == swift_role)
Sean Dague655e0af2014-05-29 09:00:22 -0400337 LOG.debug(USERS[user])
338 try:
339 admin.identity.assign_user_role(
340 USERS[user]['tenant_id'],
341 USERS[user]['id'],
342 role['id'])
Masayuki Igawad9388762015-01-20 14:56:42 +0900343 except lib_exc.Conflict:
Sean Dague655e0af2014-05-29 09:00:22 -0400344 # don't care if it's already assigned
345 pass
346
347
348def create_users(users):
349 """Create tenants from resource definition.
350
351 Don't create the tenants if they already exist.
352 """
353 global USERS
354 LOG.info("Creating users")
355 admin = keystone_admin()
356 for u in users:
357 try:
Ken'ichi Ohmichi6ea3f982015-11-09 12:41:13 +0000358 tenant = identity.get_tenant_by_name(admin.identity, u['tenant'])
Masayuki Igawabfa07602015-01-20 18:47:17 +0900359 except lib_exc.NotFound:
Sean Dague655e0af2014-05-29 09:00:22 -0400360 LOG.error("Tenant: %s - not found" % u['tenant'])
361 continue
362 try:
Ken'ichi Ohmichid9fed312015-11-09 13:05:32 +0000363 identity.get_user_by_username(admin.identity,
364 tenant['id'], u['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400365 LOG.warn("User '%s' already exists in this environment"
366 % u['name'])
Masayuki Igawabfa07602015-01-20 18:47:17 +0900367 except lib_exc.NotFound:
Sean Dague655e0af2014-05-29 09:00:22 -0400368 admin.identity.create_user(
369 u['name'], u['pass'], tenant['id'],
370 "%s@%s" % (u['name'], tenant['id']),
371 enabled=True)
372
373
Emilien Macchibb71e072014-07-05 19:18:52 +0200374def destroy_users(users):
375 admin = keystone_admin()
376 for user in users:
Ken'ichi Ohmichi6ea3f982015-11-09 12:41:13 +0000377 tenant_id = identity.get_tenant_by_name(admin.identity,
378 user['tenant'])['id']
Ken'ichi Ohmichid9fed312015-11-09 13:05:32 +0000379 user_id = identity.get_user_by_username(admin.identity,
380 tenant_id, user['name'])['id']
David Kranzb7afa922014-12-30 10:56:26 -0500381 admin.identity.delete_user(user_id)
Emilien Macchibb71e072014-07-05 19:18:52 +0200382
383
Sean Dague655e0af2014-05-29 09:00:22 -0400384def collect_users(users):
385 global USERS
Joe Gordon618c9fb2014-07-16 15:40:01 +0200386 LOG.info("Collecting users")
Sean Dague655e0af2014-05-29 09:00:22 -0400387 admin = keystone_admin()
388 for u in users:
Ken'ichi Ohmichi6ea3f982015-11-09 12:41:13 +0000389 tenant = identity.get_tenant_by_name(admin.identity, u['tenant'])
Sean Dague655e0af2014-05-29 09:00:22 -0400390 u['tenant_id'] = tenant['id']
391 USERS[u['name']] = u
Ken'ichi Ohmichid9fed312015-11-09 13:05:32 +0000392 body = identity.get_user_by_username(admin.identity,
393 tenant['id'], u['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400394 USERS[u['name']]['id'] = body['id']
395
396
397class JavelinCheck(unittest.TestCase):
398 def __init__(self, users, resources):
399 super(JavelinCheck, self).__init__()
400 self.users = users
401 self.res = resources
402
403 def runTest(self, *args):
404 pass
405
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200406 def _ping_ip(self, ip_addr, count, namespace=None):
407 if namespace is None:
408 ping_cmd = "ping -c1 " + ip_addr
409 else:
410 ping_cmd = "sudo ip netns exec %s ping -c1 %s" % (namespace,
411 ip_addr)
412 for current in range(count):
413 return_code = os.system(ping_cmd)
414 if return_code is 0:
415 break
416 self.assertNotEqual(current, count - 1,
417 "Server is not pingable at %s" % ip_addr)
418
Sean Dague655e0af2014-05-29 09:00:22 -0400419 def check(self):
420 self.check_users()
421 self.check_objects()
422 self.check_servers()
Emilien Macchid18fec12014-09-15 14:32:54 -0400423 self.check_volumes()
Chris Dent878f3782014-06-30 17:04:15 +0100424 self.check_telemetry()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200425 self.check_secgroups()
426
427 # validate neutron is enabled and ironic disabled:
428 # Tenant network isolation is not supported when using ironic.
429 # "admin" has set up a neutron flat network environment within a shared
430 # fixed network for all tenants to use.
431 # In this case, network/subnet/router creation can be skipped and the
432 # server booted the same as nova network.
433 if (CONF.service_available.neutron and
434 not CONF.baremetal.driver_enabled):
435 self.check_networking()
Sean Dague655e0af2014-05-29 09:00:22 -0400436
437 def check_users(self):
438 """Check that the users we expect to exist, do.
439
440 We don't use the resource list for this because we need to validate
441 that things like tenantId didn't drift across versions.
442 """
Joe Gordon618c9fb2014-07-16 15:40:01 +0200443 LOG.info("checking users")
Matthew Treinish71426682015-04-23 11:19:38 -0400444 for name, user in six.iteritems(self.users):
Sean Dague655e0af2014-05-29 09:00:22 -0400445 client = keystone_admin()
Ken'ichi Ohmichi402b8752015-11-09 10:47:16 +0000446 found = client.identity.show_user(user['id'])['user']
Sean Dague655e0af2014-05-29 09:00:22 -0400447 self.assertEqual(found['name'], user['name'])
448 self.assertEqual(found['tenantId'], user['tenant_id'])
449
450 # also ensure we can auth with that user, and do something
451 # on the cloud. We don't care about the results except that it
452 # remains authorized.
453 client = client_for_user(user['name'])
David Kranzae99b9a2015-02-16 13:37:01 -0500454 client.servers.list_servers()
Sean Dague655e0af2014-05-29 09:00:22 -0400455
456 def check_objects(self):
457 """Check that the objects created are still there."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000458 if not self.res.get('objects'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000459 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200460 LOG.info("checking objects")
Sean Dague655e0af2014-05-29 09:00:22 -0400461 for obj in self.res['objects']:
462 client = client_for_user(obj['owner'])
463 r, contents = client.objects.get_object(
464 obj['container'], obj['name'])
465 source = _file_contents(obj['file'])
466 self.assertEqual(contents, source)
467
468 def check_servers(self):
469 """Check that the servers are still up and running."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000470 if not self.res.get('servers'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000471 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200472 LOG.info("checking servers")
Sean Dague655e0af2014-05-29 09:00:22 -0400473 for server in self.res['servers']:
474 client = client_for_user(server['owner'])
475 found = _get_server_by_name(client, server['name'])
476 self.assertIsNotNone(
477 found,
478 "Couldn't find expected server %s" % server['name'])
479
ghanshyam0f825252015-08-25 16:02:50 +0900480 found = client.servers.show_server(found['id'])['server']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200481 # validate neutron is enabled and ironic disabled:
482 if (CONF.service_available.neutron and
483 not CONF.baremetal.driver_enabled):
Emilien Macchic3e3e292015-03-11 17:42:08 -0400484 _floating_is_alive = False
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200485 for network_name, body in found['addresses'].items():
486 for addr in body:
487 ip = addr['addr']
Emilien Macchic3e3e292015-03-11 17:42:08 -0400488 # If floatingip_for_ssh is at True, it's assumed
489 # you want to use the floating IP to reach the server,
490 # fallback to fixed IP, then other type.
491 # This is useful in multi-node environment.
armando-migliaccio2a5ac822015-03-13 19:49:55 -0700492 if CONF.compute.use_floatingip_for_ssh:
Emilien Macchic3e3e292015-03-11 17:42:08 -0400493 if addr.get('OS-EXT-IPS:type',
494 'floating') == 'floating':
495 self._ping_ip(ip, 60)
496 _floating_is_alive = True
497 elif addr.get('OS-EXT-IPS:type', 'fixed') == 'fixed':
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200498 namespace = _get_router_namespace(client,
499 network_name)
500 self._ping_ip(ip, 60, namespace)
501 else:
502 self._ping_ip(ip, 60)
Emilien Macchic3e3e292015-03-11 17:42:08 -0400503 # if floatingip_for_ssh is at True, validate found a
504 # floating IP and ping worked.
armando-migliaccio2a5ac822015-03-13 19:49:55 -0700505 if CONF.compute.use_floatingip_for_ssh:
Emilien Macchic3e3e292015-03-11 17:42:08 -0400506 self.assertTrue(_floating_is_alive,
507 "Server %s has no floating IP." %
508 server['name'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200509 else:
510 addr = found['addresses']['private'][0]['addr']
511 self._ping_ip(addr, 60)
512
513 def check_secgroups(self):
Joe H. Rahme02736732015-01-27 18:33:09 +0100514 """Check that the security groups still exist."""
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200515 LOG.info("Checking security groups")
516 for secgroup in self.res['secgroups']:
517 client = client_for_user(secgroup['owner'])
518 found = _get_resource_by_name(client.secgroups, 'security_groups',
519 secgroup['name'])
520 self.assertIsNotNone(
521 found,
522 "Couldn't find expected secgroup %s" % secgroup['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400523
Chris Dent878f3782014-06-30 17:04:15 +0100524 def check_telemetry(self):
525 """Check that ceilometer provides a sane sample.
526
nayna-patel1dfbedb2015-08-04 11:07:56 +0000527 Confirm that there is more than one sample and that they have the
Chris Dent878f3782014-06-30 17:04:15 +0100528 expected metadata.
529
530 If in check mode confirm that the oldest sample available is from
531 before the upgrade.
532 """
Chris Dent0b76f2f2014-10-10 14:24:28 +0100533 if not self.res.get('telemetry'):
534 return
Chris Dent878f3782014-06-30 17:04:15 +0100535 LOG.info("checking telemetry")
536 for server in self.res['servers']:
537 client = client_for_user(server['owner'])
David Kranz20d06f42015-02-09 14:54:15 -0500538 body = client.telemetry.list_samples(
Chris Dent878f3782014-06-30 17:04:15 +0100539 'instance',
540 query=('metadata.display_name', 'eq', server['name'])
541 )
Chris Dent878f3782014-06-30 17:04:15 +0100542 self.assertTrue(len(body) >= 1, 'expecting at least one sample')
543 self._confirm_telemetry_sample(server, body[-1])
544
Emilien Macchi626b4f82014-06-15 21:44:29 +0200545 def check_volumes(self):
546 """Check that the volumes are still there and attached."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000547 if not self.res.get('volumes'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000548 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200549 LOG.info("checking volumes")
Emilien Macchi626b4f82014-06-15 21:44:29 +0200550 for volume in self.res['volumes']:
551 client = client_for_user(volume['owner'])
Emilien Macchid18fec12014-09-15 14:32:54 -0400552 vol_body = _get_volume_by_name(client, volume['name'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200553 self.assertIsNotNone(
Emilien Macchid18fec12014-09-15 14:32:54 -0400554 vol_body,
Emilien Macchi626b4f82014-06-15 21:44:29 +0200555 "Couldn't find expected volume %s" % volume['name'])
556
557 # Verify that a volume's attachment retrieved
558 server_id = _get_server_by_name(client, volume['server'])['id']
Emilien Macchid18fec12014-09-15 14:32:54 -0400559 attachment = client.volumes.get_attachment_from_volume(vol_body)
560 self.assertEqual(vol_body['id'], attachment['volume_id'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200561 self.assertEqual(server_id, attachment['server_id'])
562
Chris Dent878f3782014-06-30 17:04:15 +0100563 def _confirm_telemetry_sample(self, server, sample):
564 """Check this sample matches the expected resource metadata."""
565 # Confirm display_name
566 self.assertEqual(server['name'],
567 sample['resource_metadata']['display_name'])
568 # Confirm instance_type of flavor
569 flavor = sample['resource_metadata'].get(
570 'flavor.name',
571 sample['resource_metadata'].get('instance_type')
572 )
573 self.assertEqual(server['flavor'], flavor)
574 # Confirm the oldest sample was created before upgrade.
575 if OPTS.mode == 'check':
576 oldest_timestamp = timeutils.normalize_time(
577 timeutils.parse_isotime(sample['timestamp']))
578 self.assertTrue(
579 oldest_timestamp < JAVELIN_START,
580 'timestamp should come before start of second javelin run'
581 )
582
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200583 def check_networking(self):
584 """Check that the networks are still there."""
585 for res_type in ('networks', 'subnets', 'routers'):
586 for res in self.res[res_type]:
587 client = client_for_user(res['owner'])
588 found = _get_resource_by_name(client.networks, res_type,
589 res['name'])
590 self.assertIsNotNone(
591 found,
592 "Couldn't find expected resource %s" % res['name'])
593
Sean Dague655e0af2014-05-29 09:00:22 -0400594
595#######################
596#
597# OBJECTS
598#
599#######################
600
601
602def _file_contents(fname):
603 with open(fname, 'r') as f:
604 return f.read()
605
606
607def create_objects(objects):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000608 if not objects:
609 return
Sean Dague655e0af2014-05-29 09:00:22 -0400610 LOG.info("Creating objects")
611 for obj in objects:
612 LOG.debug("Object %s" % obj)
Joe H. Rahme9c9fc4d2015-03-31 00:35:05 +0200613 swift_role = obj.get('swift_role', 'Member')
614 _assign_swift_role(obj['owner'], swift_role)
Sean Dague655e0af2014-05-29 09:00:22 -0400615 client = client_for_user(obj['owner'])
616 client.containers.create_container(obj['container'])
617 client.objects.create_object(
618 obj['container'], obj['name'],
619 _file_contents(obj['file']))
620
Emilien Macchibb71e072014-07-05 19:18:52 +0200621
622def destroy_objects(objects):
623 for obj in objects:
624 client = client_for_user(obj['owner'])
625 r, body = client.objects.delete_object(obj['container'], obj['name'])
Emilien Macchid70f5102014-09-10 09:54:49 -0400626 if not (200 <= int(r['status']) < 299):
Emilien Macchibb71e072014-07-05 19:18:52 +0200627 raise ValueError("unable to destroy object: [%s] %s" % (r, body))
628
629
Sean Dague655e0af2014-05-29 09:00:22 -0400630#######################
631#
632# IMAGES
633#
634#######################
635
636
Sean Dague319b37a2014-07-11 07:28:11 -0400637def _resolve_image(image, imgtype):
638 name = image[imgtype]
639 fname = os.path.join(OPTS.devstack_base, image['imgdir'], name)
640 return name, fname
641
642
Joe Gordon6f0426c2014-07-25 01:10:28 +0000643def _get_image_by_name(client, name):
Ken'ichi Ohmichie3acc122015-05-22 00:32:54 +0000644 body = client.images.list_images()
Joe Gordon6f0426c2014-07-25 01:10:28 +0000645 for image in body:
646 if name == image['name']:
647 return image
648 return None
649
650
Sean Dague655e0af2014-05-29 09:00:22 -0400651def create_images(images):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000652 if not images:
653 return
Joe Gordona18d6862014-07-24 22:55:46 +0000654 LOG.info("Creating images")
Sean Dague655e0af2014-05-29 09:00:22 -0400655 for image in images:
656 client = client_for_user(image['owner'])
657
Joe H. Rahme4352d5d2015-03-09 17:41:18 +0100658 # DEPRECATED: 'format' was used for ami images
659 # Use 'disk_format' and 'container_format' instead
660 if 'format' in image:
661 LOG.warning("Deprecated: 'format' is deprecated for images "
662 "description. Please use 'disk_format' and 'container_"
663 "format' instead.")
664 image['disk_format'] = image['format']
665 image['container_format'] = image['format']
666
Sean Dague655e0af2014-05-29 09:00:22 -0400667 # only upload a new image if the name isn't there
Joe Gordon6f0426c2014-07-25 01:10:28 +0000668 if _get_image_by_name(client, image['name']):
Joe Gordona18d6862014-07-24 22:55:46 +0000669 LOG.info("Image '%s' already exists" % image['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400670 continue
671
672 # special handling for 3 part image
673 extras = {}
Joe H. Rahme4352d5d2015-03-09 17:41:18 +0100674 if image['disk_format'] == 'ami':
Sean Dague319b37a2014-07-11 07:28:11 -0400675 name, fname = _resolve_image(image, 'aki')
David Kranz34f18782015-01-06 13:43:55 -0500676 aki = client.images.create_image(
Sean Dague319b37a2014-07-11 07:28:11 -0400677 'javelin_' + name, 'aki', 'aki')
Ken'ichi Ohmichi66494e92015-06-08 04:28:10 +0000678 client.images.store_image_file(aki.get('id'), open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400679 extras['kernel_id'] = aki.get('id')
680
Sean Dague319b37a2014-07-11 07:28:11 -0400681 name, fname = _resolve_image(image, 'ari')
David Kranz34f18782015-01-06 13:43:55 -0500682 ari = client.images.create_image(
Sean Dague319b37a2014-07-11 07:28:11 -0400683 'javelin_' + name, 'ari', 'ari')
Ken'ichi Ohmichi66494e92015-06-08 04:28:10 +0000684 client.images.store_image_file(ari.get('id'), open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400685 extras['ramdisk_id'] = ari.get('id')
686
Sean Dague319b37a2014-07-11 07:28:11 -0400687 _, fname = _resolve_image(image, 'file')
David Kranz34f18782015-01-06 13:43:55 -0500688 body = client.images.create_image(
Joe H. Rahme4352d5d2015-03-09 17:41:18 +0100689 image['name'], image['container_format'],
690 image['disk_format'], **extras)
Sean Dague655e0af2014-05-29 09:00:22 -0400691 image_id = body.get('id')
Ken'ichi Ohmichi66494e92015-06-08 04:28:10 +0000692 client.images.store_image_file(image_id, open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400693
694
Joe Gordon6f0426c2014-07-25 01:10:28 +0000695def destroy_images(images):
696 if not images:
697 return
698 LOG.info("Destroying images")
699 for image in images:
700 client = client_for_user(image['owner'])
701
702 response = _get_image_by_name(client, image['name'])
703 if not response:
nayna-patel1dfbedb2015-08-04 11:07:56 +0000704 LOG.info("Image '%s' does not exist" % image['name'])
Joe Gordon6f0426c2014-07-25 01:10:28 +0000705 continue
706 client.images.delete_image(response['id'])
707
708
Sean Dague655e0af2014-05-29 09:00:22 -0400709#######################
710#
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200711# NETWORKS
712#
713#######################
714
715def _get_router_namespace(client, network):
716 network_id = _get_resource_by_name(client.networks,
717 'networks', network)['id']
David Kranz34e88122014-12-11 15:24:05 -0500718 n_body = client.networks.list_routers()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200719 for router in n_body['routers']:
720 router_id = router['id']
David Kranz34e88122014-12-11 15:24:05 -0500721 r_body = client.networks.list_router_interfaces(router_id)
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200722 for port in r_body['ports']:
723 if port['network_id'] == network_id:
724 return "qrouter-%s" % router_id
725
726
727def _get_resource_by_name(client, resource, name):
728 get_resources = getattr(client, 'list_%s' % resource)
729 if get_resources is None:
730 raise AttributeError("client doesn't have method list_%s" % resource)
David Kranz34e88122014-12-11 15:24:05 -0500731 # Until all tempest client methods are changed to return only one value,
732 # we cannot assume they all have the same signature so we need to discard
733 # the unused response first value it two values are being returned.
734 body = get_resources()
735 if type(body) == tuple:
736 body = body[1]
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200737 if isinstance(body, dict):
738 body = body[resource]
739 for res in body:
740 if name == res['name']:
741 return res
742 raise ValueError('%s not found in %s resources' % (name, resource))
743
744
745def create_networks(networks):
746 LOG.info("Creating networks")
747 for network in networks:
748 client = client_for_user(network['owner'])
749
750 # only create a network if the name isn't here
David Kranz34e88122014-12-11 15:24:05 -0500751 body = client.networks.list_networks()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200752 if any(item['name'] == network['name'] for item in body['networks']):
nayna-patel1dfbedb2015-08-04 11:07:56 +0000753 LOG.warning("Duplicated network name: %s" % network['name'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200754 continue
755
756 client.networks.create_network(name=network['name'])
757
758
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100759def destroy_networks(networks):
760 LOG.info("Destroying subnets")
761 for network in networks:
762 client = client_for_user(network['owner'])
763 network_id = _get_resource_by_name(client.networks, 'networks',
764 network['name'])['id']
765 client.networks.delete_network(network_id)
766
767
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200768def create_subnets(subnets):
769 LOG.info("Creating subnets")
770 for subnet in subnets:
771 client = client_for_user(subnet['owner'])
772
773 network = _get_resource_by_name(client.networks, 'networks',
774 subnet['network'])
775 ip_version = netaddr.IPNetwork(subnet['range']).version
776 # ensure we don't overlap with another subnet in the network
777 try:
778 client.networks.create_subnet(network_id=network['id'],
779 cidr=subnet['range'],
780 name=subnet['name'],
781 ip_version=ip_version)
Masayuki Igawa4b29e472015-02-16 10:41:54 +0900782 except lib_exc.BadRequest as e:
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200783 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
784 if not is_overlapping_cidr:
785 raise
786
787
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100788def destroy_subnets(subnets):
789 LOG.info("Destroying subnets")
790 for subnet in subnets:
791 client = client_for_user(subnet['owner'])
John Warren3961acd2015-10-02 14:38:53 -0400792 subnet_id = _get_resource_by_name(client.subnets,
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100793 'subnets', subnet['name'])['id']
John Warren3961acd2015-10-02 14:38:53 -0400794 client.subnets.delete_subnet(subnet_id)
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100795
796
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200797def create_routers(routers):
798 LOG.info("Creating routers")
799 for router in routers:
800 client = client_for_user(router['owner'])
801
802 # only create a router if the name isn't here
David Kranz34e88122014-12-11 15:24:05 -0500803 body = client.networks.list_routers()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200804 if any(item['name'] == router['name'] for item in body['routers']):
nayna-patel1dfbedb2015-08-04 11:07:56 +0000805 LOG.warning("Duplicated router name: %s" % router['name'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200806 continue
807
808 client.networks.create_router(router['name'])
809
810
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100811def destroy_routers(routers):
812 LOG.info("Destroying routers")
813 for router in routers:
814 client = client_for_user(router['owner'])
815 router_id = _get_resource_by_name(client.networks,
816 'routers', router['name'])['id']
817 for subnet in router['subnet']:
818 subnet_id = _get_resource_by_name(client.networks,
819 'subnets', subnet)['id']
820 client.networks.remove_router_interface_with_subnet_id(router_id,
821 subnet_id)
822 client.networks.delete_router(router_id)
823
824
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200825def add_router_interface(routers):
826 for router in routers:
827 client = client_for_user(router['owner'])
828 router_id = _get_resource_by_name(client.networks,
829 'routers', router['name'])['id']
830
831 for subnet in router['subnet']:
832 subnet_id = _get_resource_by_name(client.networks,
833 'subnets', subnet)['id']
834 # connect routers to their subnets
835 client.networks.add_router_interface_with_subnet_id(router_id,
836 subnet_id)
nayna-patel1dfbedb2015-08-04 11:07:56 +0000837 # connect routers to external network if set to "gateway"
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200838 if router['gateway']:
839 if CONF.network.public_network_id:
840 ext_net = CONF.network.public_network_id
841 client.networks._update_router(
842 router_id, set_enable_snat=True,
843 external_gateway_info={"network_id": ext_net})
844 else:
845 raise ValueError('public_network_id is not configured.')
846
847
848#######################
849#
Sean Dague655e0af2014-05-29 09:00:22 -0400850# SERVERS
851#
852#######################
853
854def _get_server_by_name(client, name):
David Kranzae99b9a2015-02-16 13:37:01 -0500855 body = client.servers.list_servers()
Sean Dague655e0af2014-05-29 09:00:22 -0400856 for server in body['servers']:
857 if name == server['name']:
858 return server
859 return None
860
861
Sean Dague655e0af2014-05-29 09:00:22 -0400862def _get_flavor_by_name(client, name):
ghanshyam19973be2015-08-18 15:46:42 +0900863 body = client.flavors.list_flavors()['flavors']
Sean Dague655e0af2014-05-29 09:00:22 -0400864 for flavor in body:
865 if name == flavor['name']:
866 return flavor
867 return None
868
869
870def create_servers(servers):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000871 if not servers:
872 return
Joe Gordona18d6862014-07-24 22:55:46 +0000873 LOG.info("Creating servers")
Sean Dague655e0af2014-05-29 09:00:22 -0400874 for server in servers:
875 client = client_for_user(server['owner'])
876
877 if _get_server_by_name(client, server['name']):
Joe Gordona18d6862014-07-24 22:55:46 +0000878 LOG.info("Server '%s' already exists" % server['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400879 continue
880
881 image_id = _get_image_by_name(client, server['image'])['id']
882 flavor_id = _get_flavor_by_name(client, server['flavor'])['id']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200883 # validate neutron is enabled and ironic disabled
884 kwargs = dict()
885 if (CONF.service_available.neutron and
886 not CONF.baremetal.driver_enabled and server.get('networks')):
887 get_net_id = lambda x: (_get_resource_by_name(
888 client.networks, 'networks', x)['id'])
889 kwargs['networks'] = [{'uuid': get_net_id(network)}
890 for network in server['networks']]
David Kranz0fb14292015-02-11 15:55:20 -0500891 body = client.servers.create_server(
Ken'ichi Ohmichif2d436e2015-09-03 01:13:16 +0000892 name=server['name'], imageRef=image_id, flavorRef=flavor_id,
893 **kwargs)['server']
Joe Gordon10f260b2014-07-24 23:27:19 +0000894 server_id = body['id']
895 client.servers.wait_for_server_status(server_id, 'ACTIVE')
nayna-patel1dfbedb2015-08-04 11:07:56 +0000896 # create security group(s) after server spawning
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200897 for secgroup in server['secgroups']:
898 client.servers.add_security_group(server_id, secgroup)
Emilien Macchic3e3e292015-03-11 17:42:08 -0400899 if CONF.compute.use_floatingip_for_ssh:
Joe H. Rahme9350a102015-03-29 17:39:20 +0200900 floating_ip_pool = server.get('floating_ip_pool')
901 floating_ip = client.floating_ips.create_floating_ip(
ghanshyam9a3a9a22015-08-18 17:03:55 +0900902 pool_name=floating_ip_pool)['floating_ip']
Emilien Macchic3e3e292015-03-11 17:42:08 -0400903 client.floating_ips.associate_floating_ip_to_server(
904 floating_ip['ip'], server_id)
Sean Dague655e0af2014-05-29 09:00:22 -0400905
906
Joe Gordondb63b1c2014-07-24 23:21:21 +0000907def destroy_servers(servers):
908 if not servers:
909 return
910 LOG.info("Destroying servers")
911 for server in servers:
912 client = client_for_user(server['owner'])
913
Emilien Macchidc5bae22015-03-16 08:49:02 -0400914 response = _get_server_by_name(client, server['name'])
915 if not response:
Joe Gordondb63b1c2014-07-24 23:21:21 +0000916 LOG.info("Server '%s' does not exist" % server['name'])
917 continue
918
Emilien Macchidc5bae22015-03-16 08:49:02 -0400919 # TODO(EmilienM): disassociate floating IP from server and release it.
920 client.servers.delete_server(response['id'])
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000921 waiters.wait_for_server_termination(client.servers, response['id'],
922 ignore_error=True)
Joe Gordondb63b1c2014-07-24 23:21:21 +0000923
924
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200925def create_secgroups(secgroups):
926 LOG.info("Creating security groups")
927 for secgroup in secgroups:
928 client = client_for_user(secgroup['owner'])
929
930 # only create a security group if the name isn't here
931 # i.e. a security group may be used by another server
932 # only create a router if the name isn't here
ghanshyamb610b772015-08-24 17:29:38 +0900933 body = client.secgroups.list_security_groups()['security_groups']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200934 if any(item['name'] == secgroup['name'] for item in body):
935 LOG.warning("Security group '%s' already exists" %
936 secgroup['name'])
937 continue
938
David Kranz9964b4e2015-02-06 15:45:29 -0500939 body = client.secgroups.create_security_group(
ghanshyamb610b772015-08-24 17:29:38 +0900940 name=secgroup['name'],
941 description=secgroup['description'])['security_group']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200942 secgroup_id = body['id']
943 # for each security group, create the rules
944 for rule in secgroup['rules']:
945 ip_proto, from_port, to_port, cidr = rule.split()
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +0000946 client.secrules.create_security_group_rule(
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000947 parent_group_id=secgroup_id, ip_protocol=ip_proto,
948 from_port=from_port, to_port=to_port, cidr=cidr)
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200949
950
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100951def destroy_secgroups(secgroups):
952 LOG.info("Destroying security groups")
953 for secgroup in secgroups:
954 client = client_for_user(secgroup['owner'])
955 sg_id = _get_resource_by_name(client.secgroups,
956 'security_groups',
957 secgroup['name'])
958 # sg rules are deleted automatically
959 client.secgroups.delete_security_group(sg_id['id'])
960
961
Sean Dague655e0af2014-05-29 09:00:22 -0400962#######################
963#
Emilien Macchi626b4f82014-06-15 21:44:29 +0200964# VOLUMES
965#
966#######################
967
968def _get_volume_by_name(client, name):
John Warren6177c9e2015-08-19 20:00:17 +0000969 body = client.volumes.list_volumes()['volumes']
Emilien Macchid18fec12014-09-15 14:32:54 -0400970 for volume in body:
971 if name == volume['display_name']:
Emilien Macchi626b4f82014-06-15 21:44:29 +0200972 return volume
973 return None
974
975
976def create_volumes(volumes):
Chris Dent51e76de2014-10-01 12:07:14 +0100977 if not volumes:
978 return
979 LOG.info("Creating volumes")
Emilien Macchi626b4f82014-06-15 21:44:29 +0200980 for volume in volumes:
981 client = client_for_user(volume['owner'])
982
983 # only create a volume if the name isn't here
Emilien Macchid18fec12014-09-15 14:32:54 -0400984 if _get_volume_by_name(client, volume['name']):
985 LOG.info("volume '%s' already exists" % volume['name'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200986 continue
987
Emilien Macchid18fec12014-09-15 14:32:54 -0400988 size = volume['gb']
989 v_name = volume['name']
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000990 body = client.volumes.create_volume(size=size,
John Warren6177c9e2015-08-19 20:00:17 +0000991 display_name=v_name)['volume']
Emilien Macchid18fec12014-09-15 14:32:54 -0400992 client.volumes.wait_for_volume_status(body['id'], 'available')
Emilien Macchi626b4f82014-06-15 21:44:29 +0200993
994
Emilien Macchibb71e072014-07-05 19:18:52 +0200995def destroy_volumes(volumes):
996 for volume in volumes:
997 client = client_for_user(volume['owner'])
998 volume_id = _get_volume_by_name(client, volume['name'])['id']
Emilien Macchi5ebc27b2014-09-15 14:30:35 -0400999 client.volumes.detach_volume(volume_id)
1000 client.volumes.delete_volume(volume_id)
Emilien Macchibb71e072014-07-05 19:18:52 +02001001
1002
Emilien Macchi626b4f82014-06-15 21:44:29 +02001003def attach_volumes(volumes):
1004 for volume in volumes:
1005 client = client_for_user(volume['owner'])
Emilien Macchi626b4f82014-06-15 21:44:29 +02001006 server_id = _get_server_by_name(client, volume['server'])['id']
Emilien Macchid18fec12014-09-15 14:32:54 -04001007 volume_id = _get_volume_by_name(client, volume['name'])['id']
1008 device = volume['device']
1009 client.volumes.attach_volume(volume_id, server_id, device)
Emilien Macchi626b4f82014-06-15 21:44:29 +02001010
1011
1012#######################
1013#
Sean Dague655e0af2014-05-29 09:00:22 -04001014# MAIN LOGIC
1015#
1016#######################
1017
1018def create_resources():
1019 LOG.info("Creating Resources")
1020 # first create keystone level resources, and we need to be admin
nayna-patel1dfbedb2015-08-04 11:07:56 +00001021 # for this.
Sean Dague655e0af2014-05-29 09:00:22 -04001022 create_tenants(RES['tenants'])
1023 create_users(RES['users'])
1024 collect_users(RES['users'])
1025
1026 # next create resources in a well known order
1027 create_objects(RES['objects'])
1028 create_images(RES['images'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +02001029
1030 # validate neutron is enabled and ironic is disabled
1031 if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
1032 create_networks(RES['networks'])
1033 create_subnets(RES['subnets'])
1034 create_routers(RES['routers'])
1035 add_router_interface(RES['routers'])
1036
1037 create_secgroups(RES['secgroups'])
Emilien Macchid18fec12014-09-15 14:32:54 -04001038 create_volumes(RES['volumes'])
Joe H. Rahmec96129b2015-03-30 11:23:31 +02001039
1040 # Only attempt attaching the volumes if servers are defined in the
nayna-patel1dfbedb2015-08-04 11:07:56 +00001041 # resource file
Joe H. Rahmec96129b2015-03-30 11:23:31 +02001042 if 'servers' in RES:
1043 create_servers(RES['servers'])
1044 attach_volumes(RES['volumes'])
Sean Dague655e0af2014-05-29 09:00:22 -04001045
1046
Joe Gordondb63b1c2014-07-24 23:21:21 +00001047def destroy_resources():
1048 LOG.info("Destroying Resources")
1049 # Destroy in inverse order of create
Joe Gordondb63b1c2014-07-24 23:21:21 +00001050 destroy_servers(RES['servers'])
Joe Gordon6f0426c2014-07-25 01:10:28 +00001051 destroy_images(RES['images'])
Emilien Macchibb71e072014-07-05 19:18:52 +02001052 destroy_objects(RES['objects'])
Emilien Macchibb71e072014-07-05 19:18:52 +02001053 destroy_volumes(RES['volumes'])
Jakub Libosvar3791ac92014-11-11 13:23:44 +01001054 if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
1055 destroy_routers(RES['routers'])
1056 destroy_subnets(RES['subnets'])
1057 destroy_networks(RES['networks'])
1058 destroy_secgroups(RES['secgroups'])
Emilien Macchibb71e072014-07-05 19:18:52 +02001059 destroy_users(RES['users'])
1060 destroy_tenants(RES['tenants'])
Joe Gordon6f0426c2014-07-25 01:10:28 +00001061 LOG.warn("Destroy mode incomplete")
1062
Joe Gordondb63b1c2014-07-24 23:21:21 +00001063
Sean Dague655e0af2014-05-29 09:00:22 -04001064def get_options():
1065 global OPTS
1066 parser = argparse.ArgumentParser(
1067 description='Create and validate a fixed set of OpenStack resources')
1068 parser.add_argument('-m', '--mode',
1069 metavar='<create|check|destroy>',
1070 required=True,
1071 help=('One of (create, check, destroy)'))
1072 parser.add_argument('-r', '--resources',
1073 required=True,
1074 metavar='resourcefile.yaml',
1075 help='Resources definition yaml file')
Joe Gordon28a84ae2014-07-17 15:38:28 +00001076
Sean Dague319b37a2014-07-11 07:28:11 -04001077 parser.add_argument(
1078 '-d', '--devstack-base',
1079 required=True,
1080 metavar='/opt/stack/old',
1081 help='Devstack base directory for retrieving artifacts')
Joe Gordon28a84ae2014-07-17 15:38:28 +00001082 parser.add_argument(
1083 '-c', '--config-file',
1084 metavar='/etc/tempest.conf',
1085 help='path to javelin2(tempest) config file')
1086
Sean Dague655e0af2014-05-29 09:00:22 -04001087 # auth bits, letting us also just source the devstack openrc
1088 parser.add_argument('--os-username',
1089 metavar='<auth-user-name>',
1090 default=os.environ.get('OS_USERNAME'),
1091 help=('Defaults to env[OS_USERNAME].'))
1092 parser.add_argument('--os-password',
1093 metavar='<auth-password>',
1094 default=os.environ.get('OS_PASSWORD'),
1095 help=('Defaults to env[OS_PASSWORD].'))
1096 parser.add_argument('--os-tenant-name',
1097 metavar='<auth-tenant-name>',
1098 default=os.environ.get('OS_TENANT_NAME'),
1099 help=('Defaults to env[OS_TENANT_NAME].'))
1100
1101 OPTS = parser.parse_args()
1102 if OPTS.mode not in ('create', 'check', 'destroy'):
1103 print("ERROR: Unknown mode -m %s\n" % OPTS.mode)
1104 parser.print_help()
1105 sys.exit(1)
Joe Gordon28a84ae2014-07-17 15:38:28 +00001106 if OPTS.config_file:
1107 config.CONF.set_config_path(OPTS.config_file)
Sean Dague655e0af2014-05-29 09:00:22 -04001108
1109
Joe Gordon915eb8e2014-07-17 11:25:46 +02001110def setup_logging():
Sean Dague655e0af2014-05-29 09:00:22 -04001111 global LOG
Doug Hellmann583ce2c2015-03-11 14:55:46 +00001112 logging.setup(CONF, __name__)
Sean Dague655e0af2014-05-29 09:00:22 -04001113 LOG = logging.getLogger(__name__)
Sean Dague655e0af2014-05-29 09:00:22 -04001114
1115
1116def main():
1117 global RES
1118 get_options()
1119 setup_logging()
Chris Dent51e76de2014-10-01 12:07:14 +01001120 RES.update(load_resources(OPTS.resources))
Sean Dague655e0af2014-05-29 09:00:22 -04001121
1122 if OPTS.mode == 'create':
1123 create_resources()
Joe Gordon1a097002014-07-24 23:44:08 +00001124 # Make sure the resources we just created actually work
1125 checker = JavelinCheck(USERS, RES)
1126 checker.check()
Sean Dague655e0af2014-05-29 09:00:22 -04001127 elif OPTS.mode == 'check':
1128 collect_users(RES['users'])
1129 checker = JavelinCheck(USERS, RES)
1130 checker.check()
1131 elif OPTS.mode == 'destroy':
Joe Gordondb63b1c2014-07-24 23:21:21 +00001132 collect_users(RES['users'])
1133 destroy_resources()
Sean Dague655e0af2014-05-29 09:00:22 -04001134 else:
1135 LOG.error('Unknown mode %s' % OPTS.mode)
1136 return 1
Joe Gordon246353a2014-07-18 00:10:28 +02001137 LOG.info('javelin2 successfully finished')
Sean Dague655e0af2014-05-29 09:00:22 -04001138 return 0
1139
1140if __name__ == "__main__":
1141 sys.exit(main())