blob: 826d8e8550fb5b57a8af4f6f0a699e745615764c [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 Melladob04da902015-11-20 17:43:12 +0100131from tempest.services.identity.v2.json import tenants_client
Ken'ichi Ohmichi69dcf442015-11-30 11:48:01 +0000132from tempest.services.image.v2.json import images_client
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200133from tempest.services.network.json import network_client
John Warren3961acd2015-10-02 14:38:53 -0400134from tempest.services.network.json import subnets_client
Sean Dague655e0af2014-05-29 09:00:22 -0400135from tempest.services.object_storage import container_client
136from tempest.services.object_storage import object_client
liu-sheng67b730e2015-07-16 15:19:33 +0800137from tempest.services.telemetry.json import alarming_client
Chris Dent878f3782014-06-30 17:04:15 +0100138from tempest.services.telemetry.json import telemetry_client
Yaroslav Lobankovdb4a2e12015-11-28 20:04:54 +0300139from tempest.services.volume.v1.json import volumes_client
Sean Dague655e0af2014-05-29 09:00:22 -0400140
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200141CONF = config.CONF
Sean Dague655e0af2014-05-29 09:00:22 -0400142OPTS = {}
143USERS = {}
Chris Dent51e76de2014-10-01 12:07:14 +0100144RES = collections.defaultdict(list)
Sean Dague655e0af2014-05-29 09:00:22 -0400145
146LOG = None
147
Chris Dent878f3782014-06-30 17:04:15 +0100148JAVELIN_START = datetime.datetime.utcnow()
149
Sean Dague655e0af2014-05-29 09:00:22 -0400150
151class OSClient(object):
152 _creds = None
153 identity = None
154 servers = None
155
156 def __init__(self, user, pw, tenant):
Ken'ichi Ohmichi4771cbc2015-01-19 23:45:23 +0000157 default_params = {
158 'disable_ssl_certificate_validation':
159 CONF.identity.disable_ssl_certificate_validation,
160 'ca_certs': CONF.identity.ca_certificates_file,
161 'trace_requests': CONF.debug.trace_requests
162 }
Ken'ichi Ohmichid5dba1c2015-01-23 02:23:22 +0000163 default_params_with_timeout_values = {
164 'build_interval': CONF.compute.build_interval,
165 'build_timeout': CONF.compute.build_timeout
166 }
167 default_params_with_timeout_values.update(default_params)
168
Ken'ichi Ohmichi4771cbc2015-01-19 23:45:23 +0000169 compute_params = {
170 'service': CONF.compute.catalog_type,
171 'region': CONF.compute.region or CONF.identity.region,
172 'endpoint_type': CONF.compute.endpoint_type,
173 'build_interval': CONF.compute.build_interval,
174 'build_timeout': CONF.compute.build_timeout
175 }
176 compute_params.update(default_params)
177
Ken'ichi Ohmichi564b2ad2015-01-22 02:08:59 +0000178 object_storage_params = {
179 'service': CONF.object_storage.catalog_type,
180 'region': CONF.object_storage.region or CONF.identity.region,
181 'endpoint_type': CONF.object_storage.endpoint_type
182 }
183 object_storage_params.update(default_params)
184
andreafb8a52282015-03-19 22:21:54 +0000185 _creds = auth.KeystoneV2Credentials(
Sean Dague655e0af2014-05-29 09:00:22 -0400186 username=user,
187 password=pw,
188 tenant_name=tenant)
Andrea Frittoli90012352015-02-25 21:58:02 +0000189 auth_provider_params = {
190 'disable_ssl_certificate_validation':
191 CONF.identity.disable_ssl_certificate_validation,
192 'ca_certs': CONF.identity.ca_certificates_file,
193 'trace_requests': CONF.debug.trace_requests
194 }
andreafb8a52282015-03-19 22:21:54 +0000195 _auth = auth.KeystoneV2AuthProvider(
Andrea Frittoli90012352015-02-25 21:58:02 +0000196 _creds, CONF.identity.uri, **auth_provider_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000197 self.identity = identity_client.IdentityClient(
ghanshyamd26b5cd2015-02-09 14:48:58 +0900198 _auth,
199 CONF.identity.catalog_type,
200 CONF.identity.region,
201 endpoint_type='adminURL',
202 **default_params_with_timeout_values)
Daniel Melladob04da902015-11-20 17:43:12 +0100203 self.tenants = tenants_client.TenantsClient(
204 _auth,
205 CONF.identity.catalog_type,
206 CONF.identity.region,
207 endpoint_type='adminURL',
208 **default_params_with_timeout_values)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000209 self.servers = servers_client.ServersClient(_auth,
210 **compute_params)
211 self.flavors = flavors_client.FlavorsClient(_auth,
212 **compute_params)
213 self.floating_ips = floating_ips_client.FloatingIPsClient(
Emilien Macchic3e3e292015-03-11 17:42:08 -0400214 _auth, **compute_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000215 self.secgroups = security_groups_client.SecurityGroupsClient(
Ken'ichi Ohmichi4771cbc2015-01-19 23:45:23 +0000216 _auth, **compute_params)
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +0000217 self.secrules = security_group_rules_client.SecurityGroupRulesClient(
218 _auth, **compute_params)
Ken'ichi Ohmichi564b2ad2015-01-22 02:08:59 +0000219 self.objects = object_client.ObjectClient(_auth,
220 **object_storage_params)
221 self.containers = container_client.ContainerClient(
222 _auth, **object_storage_params)
Ken'ichi Ohmichi69dcf442015-11-30 11:48:01 +0000223 self.images = images_client.ImagesClientV2(
Masayuki Igawabc7e1892015-03-03 11:46:48 +0900224 _auth,
225 CONF.image.catalog_type,
226 CONF.image.region or CONF.identity.region,
227 endpoint_type=CONF.image.endpoint_type,
228 build_interval=CONF.image.build_interval,
229 build_timeout=CONF.image.build_timeout,
230 **default_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000231 self.telemetry = telemetry_client.TelemetryClient(
Ken'ichi Ohmichid5dba1c2015-01-23 02:23:22 +0000232 _auth,
233 CONF.telemetry.catalog_type,
234 CONF.identity.region,
235 endpoint_type=CONF.telemetry.endpoint_type,
236 **default_params_with_timeout_values)
liu-sheng67b730e2015-07-16 15:19:33 +0800237 self.alarming = alarming_client.AlarmingClient(
238 _auth,
239 CONF.alarm.catalog_type,
240 CONF.identity.region,
241 endpoint_type=CONF.alarm.endpoint_type,
242 **default_params_with_timeout_values)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000243 self.volumes = volumes_client.VolumesClient(
Ken'ichi Ohmichif85e9bd2015-01-27 12:51:47 +0000244 _auth,
245 CONF.volume.catalog_type,
246 CONF.volume.region or CONF.identity.region,
247 endpoint_type=CONF.volume.endpoint_type,
248 build_interval=CONF.volume.build_interval,
249 build_timeout=CONF.volume.build_timeout,
250 **default_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000251 self.networks = network_client.NetworkClient(
Ken'ichi Ohmichia182e862015-01-21 01:16:37 +0000252 _auth,
253 CONF.network.catalog_type,
254 CONF.network.region or CONF.identity.region,
255 endpoint_type=CONF.network.endpoint_type,
256 build_interval=CONF.network.build_interval,
257 build_timeout=CONF.network.build_timeout,
258 **default_params)
John Warren3961acd2015-10-02 14:38:53 -0400259 self.subnets = subnets_client.SubnetsClient(
260 _auth,
261 CONF.network.catalog_type,
262 CONF.network.region or CONF.identity.region,
263 endpoint_type=CONF.network.endpoint_type,
264 build_interval=CONF.network.build_interval,
265 build_timeout=CONF.network.build_timeout,
266 **default_params)
Sean Dague655e0af2014-05-29 09:00:22 -0400267
268
269def load_resources(fname):
Joe H. Rahme02736732015-01-27 18:33:09 +0100270 """Load the expected resources from a yaml file."""
Sean Dague655e0af2014-05-29 09:00:22 -0400271 return yaml.load(open(fname, 'r'))
272
273
274def keystone_admin():
275 return OSClient(OPTS.os_username, OPTS.os_password, OPTS.os_tenant_name)
276
277
278def client_for_user(name):
279 LOG.debug("Entering client_for_user")
280 if name in USERS:
281 user = USERS[name]
282 LOG.debug("Created client for user %s" % user)
283 return OSClient(user['name'], user['pass'], user['tenant'])
284 else:
285 LOG.error("%s not found in USERS: %s" % (name, USERS))
286
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200287
Sean Dague655e0af2014-05-29 09:00:22 -0400288###################
289#
290# TENANTS
291#
292###################
293
294
295def create_tenants(tenants):
296 """Create tenants from resource definition.
297
298 Don't create the tenants if they already exist.
299 """
300 admin = keystone_admin()
Daniel Melladob04da902015-11-20 17:43:12 +0100301 body = admin.tenants.list_tenants()['tenants']
Sean Dague655e0af2014-05-29 09:00:22 -0400302 existing = [x['name'] for x in body]
303 for tenant in tenants:
304 if tenant not in existing:
Daniel Melladob04da902015-11-20 17:43:12 +0100305 admin.tenants.create_tenant(tenant)['tenant']
Sean Dague655e0af2014-05-29 09:00:22 -0400306 else:
307 LOG.warn("Tenant '%s' already exists in this environment" % tenant)
308
Emilien Macchibb71e072014-07-05 19:18:52 +0200309
310def destroy_tenants(tenants):
311 admin = keystone_admin()
312 for tenant in tenants:
Daniel Melladob04da902015-11-20 17:43:12 +0100313 tenant_id = identity.get_tenant_by_name(admin.tenant, tenant)['id']
314 admin.tenants.delete_tenant(tenant_id)
Emilien Macchibb71e072014-07-05 19:18:52 +0200315
Sean Dague655e0af2014-05-29 09:00:22 -0400316##############
317#
318# USERS
319#
320##############
321
322
323def _users_for_tenant(users, tenant):
324 u_for_t = []
325 for user in users:
326 for n in user:
327 if user[n]['tenant'] == tenant:
328 u_for_t.append(user[n])
329 return u_for_t
330
331
332def _tenants_from_users(users):
333 tenants = set()
334 for user in users:
335 for n in user:
336 tenants.add(user[n]['tenant'])
337 return tenants
338
339
Joe H. Rahme9c9fc4d2015-03-31 00:35:05 +0200340def _assign_swift_role(user, swift_role):
Sean Dague655e0af2014-05-29 09:00:22 -0400341 admin = keystone_admin()
David Kranzb7afa922014-12-30 10:56:26 -0500342 roles = admin.identity.list_roles()
Joe H. Rahme9c9fc4d2015-03-31 00:35:05 +0200343 role = next(r for r in roles if r['name'] == swift_role)
Sean Dague655e0af2014-05-29 09:00:22 -0400344 LOG.debug(USERS[user])
345 try:
346 admin.identity.assign_user_role(
347 USERS[user]['tenant_id'],
348 USERS[user]['id'],
349 role['id'])
Masayuki Igawad9388762015-01-20 14:56:42 +0900350 except lib_exc.Conflict:
Sean Dague655e0af2014-05-29 09:00:22 -0400351 # don't care if it's already assigned
352 pass
353
354
355def create_users(users):
356 """Create tenants from resource definition.
357
358 Don't create the tenants if they already exist.
359 """
360 global USERS
361 LOG.info("Creating users")
362 admin = keystone_admin()
363 for u in users:
364 try:
Daniel Melladob04da902015-11-20 17:43:12 +0100365 tenant = identity.get_tenant_by_name(admin.tenants, u['tenant'])
Masayuki Igawabfa07602015-01-20 18:47:17 +0900366 except lib_exc.NotFound:
Sean Dague655e0af2014-05-29 09:00:22 -0400367 LOG.error("Tenant: %s - not found" % u['tenant'])
368 continue
369 try:
Ken'ichi Ohmichid9fed312015-11-09 13:05:32 +0000370 identity.get_user_by_username(admin.identity,
371 tenant['id'], u['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400372 LOG.warn("User '%s' already exists in this environment"
373 % u['name'])
Masayuki Igawabfa07602015-01-20 18:47:17 +0900374 except lib_exc.NotFound:
Sean Dague655e0af2014-05-29 09:00:22 -0400375 admin.identity.create_user(
376 u['name'], u['pass'], tenant['id'],
377 "%s@%s" % (u['name'], tenant['id']),
378 enabled=True)
379
380
Emilien Macchibb71e072014-07-05 19:18:52 +0200381def destroy_users(users):
382 admin = keystone_admin()
383 for user in users:
Daniel Melladob04da902015-11-20 17:43:12 +0100384 tenant_id = identity.get_tenant_by_name(admin.tenants,
Ken'ichi Ohmichi6ea3f982015-11-09 12:41:13 +0000385 user['tenant'])['id']
Ken'ichi Ohmichid9fed312015-11-09 13:05:32 +0000386 user_id = identity.get_user_by_username(admin.identity,
387 tenant_id, user['name'])['id']
David Kranzb7afa922014-12-30 10:56:26 -0500388 admin.identity.delete_user(user_id)
Emilien Macchibb71e072014-07-05 19:18:52 +0200389
390
Sean Dague655e0af2014-05-29 09:00:22 -0400391def collect_users(users):
392 global USERS
Joe Gordon618c9fb2014-07-16 15:40:01 +0200393 LOG.info("Collecting users")
Sean Dague655e0af2014-05-29 09:00:22 -0400394 admin = keystone_admin()
395 for u in users:
Daniel Melladob04da902015-11-20 17:43:12 +0100396 tenant = identity.get_tenant_by_name(admin.tenants, u['tenant'])
Sean Dague655e0af2014-05-29 09:00:22 -0400397 u['tenant_id'] = tenant['id']
398 USERS[u['name']] = u
Ken'ichi Ohmichid9fed312015-11-09 13:05:32 +0000399 body = identity.get_user_by_username(admin.identity,
400 tenant['id'], u['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400401 USERS[u['name']]['id'] = body['id']
402
403
404class JavelinCheck(unittest.TestCase):
405 def __init__(self, users, resources):
406 super(JavelinCheck, self).__init__()
407 self.users = users
408 self.res = resources
409
410 def runTest(self, *args):
411 pass
412
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200413 def _ping_ip(self, ip_addr, count, namespace=None):
414 if namespace is None:
415 ping_cmd = "ping -c1 " + ip_addr
416 else:
417 ping_cmd = "sudo ip netns exec %s ping -c1 %s" % (namespace,
418 ip_addr)
419 for current in range(count):
420 return_code = os.system(ping_cmd)
421 if return_code is 0:
422 break
423 self.assertNotEqual(current, count - 1,
424 "Server is not pingable at %s" % ip_addr)
425
Sean Dague655e0af2014-05-29 09:00:22 -0400426 def check(self):
427 self.check_users()
428 self.check_objects()
429 self.check_servers()
Emilien Macchid18fec12014-09-15 14:32:54 -0400430 self.check_volumes()
Chris Dent878f3782014-06-30 17:04:15 +0100431 self.check_telemetry()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200432 self.check_secgroups()
433
434 # validate neutron is enabled and ironic disabled:
435 # Tenant network isolation is not supported when using ironic.
436 # "admin" has set up a neutron flat network environment within a shared
437 # fixed network for all tenants to use.
438 # In this case, network/subnet/router creation can be skipped and the
439 # server booted the same as nova network.
440 if (CONF.service_available.neutron and
441 not CONF.baremetal.driver_enabled):
442 self.check_networking()
Sean Dague655e0af2014-05-29 09:00:22 -0400443
444 def check_users(self):
445 """Check that the users we expect to exist, do.
446
447 We don't use the resource list for this because we need to validate
448 that things like tenantId didn't drift across versions.
449 """
Joe Gordon618c9fb2014-07-16 15:40:01 +0200450 LOG.info("checking users")
Matthew Treinish71426682015-04-23 11:19:38 -0400451 for name, user in six.iteritems(self.users):
Sean Dague655e0af2014-05-29 09:00:22 -0400452 client = keystone_admin()
Ken'ichi Ohmichi402b8752015-11-09 10:47:16 +0000453 found = client.identity.show_user(user['id'])['user']
Sean Dague655e0af2014-05-29 09:00:22 -0400454 self.assertEqual(found['name'], user['name'])
455 self.assertEqual(found['tenantId'], user['tenant_id'])
456
457 # also ensure we can auth with that user, and do something
458 # on the cloud. We don't care about the results except that it
459 # remains authorized.
460 client = client_for_user(user['name'])
David Kranzae99b9a2015-02-16 13:37:01 -0500461 client.servers.list_servers()
Sean Dague655e0af2014-05-29 09:00:22 -0400462
463 def check_objects(self):
464 """Check that the objects created are still there."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000465 if not self.res.get('objects'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000466 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200467 LOG.info("checking objects")
Sean Dague655e0af2014-05-29 09:00:22 -0400468 for obj in self.res['objects']:
469 client = client_for_user(obj['owner'])
470 r, contents = client.objects.get_object(
471 obj['container'], obj['name'])
472 source = _file_contents(obj['file'])
473 self.assertEqual(contents, source)
474
475 def check_servers(self):
476 """Check that the servers are still up and running."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000477 if not self.res.get('servers'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000478 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200479 LOG.info("checking servers")
Sean Dague655e0af2014-05-29 09:00:22 -0400480 for server in self.res['servers']:
481 client = client_for_user(server['owner'])
482 found = _get_server_by_name(client, server['name'])
483 self.assertIsNotNone(
484 found,
485 "Couldn't find expected server %s" % server['name'])
486
ghanshyam0f825252015-08-25 16:02:50 +0900487 found = client.servers.show_server(found['id'])['server']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200488 # validate neutron is enabled and ironic disabled:
489 if (CONF.service_available.neutron and
490 not CONF.baremetal.driver_enabled):
Emilien Macchic3e3e292015-03-11 17:42:08 -0400491 _floating_is_alive = False
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200492 for network_name, body in found['addresses'].items():
493 for addr in body:
494 ip = addr['addr']
Emilien Macchic3e3e292015-03-11 17:42:08 -0400495 # If floatingip_for_ssh is at True, it's assumed
496 # you want to use the floating IP to reach the server,
497 # fallback to fixed IP, then other type.
498 # This is useful in multi-node environment.
armando-migliaccio2a5ac822015-03-13 19:49:55 -0700499 if CONF.compute.use_floatingip_for_ssh:
Emilien Macchic3e3e292015-03-11 17:42:08 -0400500 if addr.get('OS-EXT-IPS:type',
501 'floating') == 'floating':
502 self._ping_ip(ip, 60)
503 _floating_is_alive = True
504 elif addr.get('OS-EXT-IPS:type', 'fixed') == 'fixed':
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200505 namespace = _get_router_namespace(client,
506 network_name)
507 self._ping_ip(ip, 60, namespace)
508 else:
509 self._ping_ip(ip, 60)
Emilien Macchic3e3e292015-03-11 17:42:08 -0400510 # if floatingip_for_ssh is at True, validate found a
511 # floating IP and ping worked.
armando-migliaccio2a5ac822015-03-13 19:49:55 -0700512 if CONF.compute.use_floatingip_for_ssh:
Emilien Macchic3e3e292015-03-11 17:42:08 -0400513 self.assertTrue(_floating_is_alive,
514 "Server %s has no floating IP." %
515 server['name'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200516 else:
517 addr = found['addresses']['private'][0]['addr']
518 self._ping_ip(addr, 60)
519
520 def check_secgroups(self):
Joe H. Rahme02736732015-01-27 18:33:09 +0100521 """Check that the security groups still exist."""
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200522 LOG.info("Checking security groups")
523 for secgroup in self.res['secgroups']:
524 client = client_for_user(secgroup['owner'])
525 found = _get_resource_by_name(client.secgroups, 'security_groups',
526 secgroup['name'])
527 self.assertIsNotNone(
528 found,
529 "Couldn't find expected secgroup %s" % secgroup['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400530
Chris Dent878f3782014-06-30 17:04:15 +0100531 def check_telemetry(self):
532 """Check that ceilometer provides a sane sample.
533
nayna-patel1dfbedb2015-08-04 11:07:56 +0000534 Confirm that there is more than one sample and that they have the
Chris Dent878f3782014-06-30 17:04:15 +0100535 expected metadata.
536
537 If in check mode confirm that the oldest sample available is from
538 before the upgrade.
539 """
Chris Dent0b76f2f2014-10-10 14:24:28 +0100540 if not self.res.get('telemetry'):
541 return
Chris Dent878f3782014-06-30 17:04:15 +0100542 LOG.info("checking telemetry")
543 for server in self.res['servers']:
544 client = client_for_user(server['owner'])
David Kranz20d06f42015-02-09 14:54:15 -0500545 body = client.telemetry.list_samples(
Chris Dent878f3782014-06-30 17:04:15 +0100546 'instance',
547 query=('metadata.display_name', 'eq', server['name'])
548 )
Chris Dent878f3782014-06-30 17:04:15 +0100549 self.assertTrue(len(body) >= 1, 'expecting at least one sample')
550 self._confirm_telemetry_sample(server, body[-1])
551
Emilien Macchi626b4f82014-06-15 21:44:29 +0200552 def check_volumes(self):
553 """Check that the volumes are still there and attached."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000554 if not self.res.get('volumes'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000555 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200556 LOG.info("checking volumes")
Emilien Macchi626b4f82014-06-15 21:44:29 +0200557 for volume in self.res['volumes']:
558 client = client_for_user(volume['owner'])
Emilien Macchid18fec12014-09-15 14:32:54 -0400559 vol_body = _get_volume_by_name(client, volume['name'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200560 self.assertIsNotNone(
Emilien Macchid18fec12014-09-15 14:32:54 -0400561 vol_body,
Emilien Macchi626b4f82014-06-15 21:44:29 +0200562 "Couldn't find expected volume %s" % volume['name'])
563
564 # Verify that a volume's attachment retrieved
565 server_id = _get_server_by_name(client, volume['server'])['id']
Emilien Macchid18fec12014-09-15 14:32:54 -0400566 attachment = client.volumes.get_attachment_from_volume(vol_body)
567 self.assertEqual(vol_body['id'], attachment['volume_id'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200568 self.assertEqual(server_id, attachment['server_id'])
569
Chris Dent878f3782014-06-30 17:04:15 +0100570 def _confirm_telemetry_sample(self, server, sample):
571 """Check this sample matches the expected resource metadata."""
572 # Confirm display_name
573 self.assertEqual(server['name'],
574 sample['resource_metadata']['display_name'])
575 # Confirm instance_type of flavor
576 flavor = sample['resource_metadata'].get(
577 'flavor.name',
578 sample['resource_metadata'].get('instance_type')
579 )
580 self.assertEqual(server['flavor'], flavor)
581 # Confirm the oldest sample was created before upgrade.
582 if OPTS.mode == 'check':
583 oldest_timestamp = timeutils.normalize_time(
584 timeutils.parse_isotime(sample['timestamp']))
585 self.assertTrue(
586 oldest_timestamp < JAVELIN_START,
587 'timestamp should come before start of second javelin run'
588 )
589
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200590 def check_networking(self):
591 """Check that the networks are still there."""
592 for res_type in ('networks', 'subnets', 'routers'):
593 for res in self.res[res_type]:
594 client = client_for_user(res['owner'])
595 found = _get_resource_by_name(client.networks, res_type,
596 res['name'])
597 self.assertIsNotNone(
598 found,
599 "Couldn't find expected resource %s" % res['name'])
600
Sean Dague655e0af2014-05-29 09:00:22 -0400601
602#######################
603#
604# OBJECTS
605#
606#######################
607
608
609def _file_contents(fname):
610 with open(fname, 'r') as f:
611 return f.read()
612
613
614def create_objects(objects):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000615 if not objects:
616 return
Sean Dague655e0af2014-05-29 09:00:22 -0400617 LOG.info("Creating objects")
618 for obj in objects:
619 LOG.debug("Object %s" % obj)
Joe H. Rahme9c9fc4d2015-03-31 00:35:05 +0200620 swift_role = obj.get('swift_role', 'Member')
621 _assign_swift_role(obj['owner'], swift_role)
Sean Dague655e0af2014-05-29 09:00:22 -0400622 client = client_for_user(obj['owner'])
623 client.containers.create_container(obj['container'])
624 client.objects.create_object(
625 obj['container'], obj['name'],
626 _file_contents(obj['file']))
627
Emilien Macchibb71e072014-07-05 19:18:52 +0200628
629def destroy_objects(objects):
630 for obj in objects:
631 client = client_for_user(obj['owner'])
632 r, body = client.objects.delete_object(obj['container'], obj['name'])
Emilien Macchid70f5102014-09-10 09:54:49 -0400633 if not (200 <= int(r['status']) < 299):
Emilien Macchibb71e072014-07-05 19:18:52 +0200634 raise ValueError("unable to destroy object: [%s] %s" % (r, body))
635
636
Sean Dague655e0af2014-05-29 09:00:22 -0400637#######################
638#
639# IMAGES
640#
641#######################
642
643
Sean Dague319b37a2014-07-11 07:28:11 -0400644def _resolve_image(image, imgtype):
645 name = image[imgtype]
646 fname = os.path.join(OPTS.devstack_base, image['imgdir'], name)
647 return name, fname
648
649
Joe Gordon6f0426c2014-07-25 01:10:28 +0000650def _get_image_by_name(client, name):
Ken'ichi Ohmichie3acc122015-05-22 00:32:54 +0000651 body = client.images.list_images()
Joe Gordon6f0426c2014-07-25 01:10:28 +0000652 for image in body:
653 if name == image['name']:
654 return image
655 return None
656
657
Sean Dague655e0af2014-05-29 09:00:22 -0400658def create_images(images):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000659 if not images:
660 return
Joe Gordona18d6862014-07-24 22:55:46 +0000661 LOG.info("Creating images")
Sean Dague655e0af2014-05-29 09:00:22 -0400662 for image in images:
663 client = client_for_user(image['owner'])
664
Joe H. Rahme4352d5d2015-03-09 17:41:18 +0100665 # DEPRECATED: 'format' was used for ami images
666 # Use 'disk_format' and 'container_format' instead
667 if 'format' in image:
668 LOG.warning("Deprecated: 'format' is deprecated for images "
669 "description. Please use 'disk_format' and 'container_"
670 "format' instead.")
671 image['disk_format'] = image['format']
672 image['container_format'] = image['format']
673
Sean Dague655e0af2014-05-29 09:00:22 -0400674 # only upload a new image if the name isn't there
Joe Gordon6f0426c2014-07-25 01:10:28 +0000675 if _get_image_by_name(client, image['name']):
Joe Gordona18d6862014-07-24 22:55:46 +0000676 LOG.info("Image '%s' already exists" % image['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400677 continue
678
679 # special handling for 3 part image
680 extras = {}
Joe H. Rahme4352d5d2015-03-09 17:41:18 +0100681 if image['disk_format'] == 'ami':
Sean Dague319b37a2014-07-11 07:28:11 -0400682 name, fname = _resolve_image(image, 'aki')
David Kranz34f18782015-01-06 13:43:55 -0500683 aki = client.images.create_image(
Sean Dague319b37a2014-07-11 07:28:11 -0400684 'javelin_' + name, 'aki', 'aki')
Ken'ichi Ohmichi66494e92015-06-08 04:28:10 +0000685 client.images.store_image_file(aki.get('id'), open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400686 extras['kernel_id'] = aki.get('id')
687
Sean Dague319b37a2014-07-11 07:28:11 -0400688 name, fname = _resolve_image(image, 'ari')
David Kranz34f18782015-01-06 13:43:55 -0500689 ari = client.images.create_image(
Sean Dague319b37a2014-07-11 07:28:11 -0400690 'javelin_' + name, 'ari', 'ari')
Ken'ichi Ohmichi66494e92015-06-08 04:28:10 +0000691 client.images.store_image_file(ari.get('id'), open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400692 extras['ramdisk_id'] = ari.get('id')
693
Sean Dague319b37a2014-07-11 07:28:11 -0400694 _, fname = _resolve_image(image, 'file')
David Kranz34f18782015-01-06 13:43:55 -0500695 body = client.images.create_image(
Joe H. Rahme4352d5d2015-03-09 17:41:18 +0100696 image['name'], image['container_format'],
697 image['disk_format'], **extras)
Sean Dague655e0af2014-05-29 09:00:22 -0400698 image_id = body.get('id')
Ken'ichi Ohmichi66494e92015-06-08 04:28:10 +0000699 client.images.store_image_file(image_id, open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400700
701
Joe Gordon6f0426c2014-07-25 01:10:28 +0000702def destroy_images(images):
703 if not images:
704 return
705 LOG.info("Destroying images")
706 for image in images:
707 client = client_for_user(image['owner'])
708
709 response = _get_image_by_name(client, image['name'])
710 if not response:
nayna-patel1dfbedb2015-08-04 11:07:56 +0000711 LOG.info("Image '%s' does not exist" % image['name'])
Joe Gordon6f0426c2014-07-25 01:10:28 +0000712 continue
713 client.images.delete_image(response['id'])
714
715
Sean Dague655e0af2014-05-29 09:00:22 -0400716#######################
717#
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200718# NETWORKS
719#
720#######################
721
722def _get_router_namespace(client, network):
723 network_id = _get_resource_by_name(client.networks,
724 'networks', network)['id']
David Kranz34e88122014-12-11 15:24:05 -0500725 n_body = client.networks.list_routers()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200726 for router in n_body['routers']:
727 router_id = router['id']
David Kranz34e88122014-12-11 15:24:05 -0500728 r_body = client.networks.list_router_interfaces(router_id)
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200729 for port in r_body['ports']:
730 if port['network_id'] == network_id:
731 return "qrouter-%s" % router_id
732
733
734def _get_resource_by_name(client, resource, name):
735 get_resources = getattr(client, 'list_%s' % resource)
736 if get_resources is None:
737 raise AttributeError("client doesn't have method list_%s" % resource)
David Kranz34e88122014-12-11 15:24:05 -0500738 # Until all tempest client methods are changed to return only one value,
739 # we cannot assume they all have the same signature so we need to discard
740 # the unused response first value it two values are being returned.
741 body = get_resources()
742 if type(body) == tuple:
743 body = body[1]
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200744 if isinstance(body, dict):
745 body = body[resource]
746 for res in body:
747 if name == res['name']:
748 return res
749 raise ValueError('%s not found in %s resources' % (name, resource))
750
751
752def create_networks(networks):
753 LOG.info("Creating networks")
754 for network in networks:
755 client = client_for_user(network['owner'])
756
757 # only create a network if the name isn't here
David Kranz34e88122014-12-11 15:24:05 -0500758 body = client.networks.list_networks()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200759 if any(item['name'] == network['name'] for item in body['networks']):
nayna-patel1dfbedb2015-08-04 11:07:56 +0000760 LOG.warning("Duplicated network name: %s" % network['name'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200761 continue
762
763 client.networks.create_network(name=network['name'])
764
765
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100766def destroy_networks(networks):
767 LOG.info("Destroying subnets")
768 for network in networks:
769 client = client_for_user(network['owner'])
770 network_id = _get_resource_by_name(client.networks, 'networks',
771 network['name'])['id']
772 client.networks.delete_network(network_id)
773
774
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200775def create_subnets(subnets):
776 LOG.info("Creating subnets")
777 for subnet in subnets:
778 client = client_for_user(subnet['owner'])
779
780 network = _get_resource_by_name(client.networks, 'networks',
781 subnet['network'])
782 ip_version = netaddr.IPNetwork(subnet['range']).version
783 # ensure we don't overlap with another subnet in the network
784 try:
785 client.networks.create_subnet(network_id=network['id'],
786 cidr=subnet['range'],
787 name=subnet['name'],
788 ip_version=ip_version)
Masayuki Igawa4b29e472015-02-16 10:41:54 +0900789 except lib_exc.BadRequest as e:
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200790 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
791 if not is_overlapping_cidr:
792 raise
793
794
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100795def destroy_subnets(subnets):
796 LOG.info("Destroying subnets")
797 for subnet in subnets:
798 client = client_for_user(subnet['owner'])
John Warren3961acd2015-10-02 14:38:53 -0400799 subnet_id = _get_resource_by_name(client.subnets,
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100800 'subnets', subnet['name'])['id']
John Warren3961acd2015-10-02 14:38:53 -0400801 client.subnets.delete_subnet(subnet_id)
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100802
803
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200804def create_routers(routers):
805 LOG.info("Creating routers")
806 for router in routers:
807 client = client_for_user(router['owner'])
808
809 # only create a router if the name isn't here
David Kranz34e88122014-12-11 15:24:05 -0500810 body = client.networks.list_routers()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200811 if any(item['name'] == router['name'] for item in body['routers']):
nayna-patel1dfbedb2015-08-04 11:07:56 +0000812 LOG.warning("Duplicated router name: %s" % router['name'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200813 continue
814
815 client.networks.create_router(router['name'])
816
817
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100818def destroy_routers(routers):
819 LOG.info("Destroying routers")
820 for router in routers:
821 client = client_for_user(router['owner'])
822 router_id = _get_resource_by_name(client.networks,
823 'routers', router['name'])['id']
824 for subnet in router['subnet']:
825 subnet_id = _get_resource_by_name(client.networks,
826 'subnets', subnet)['id']
827 client.networks.remove_router_interface_with_subnet_id(router_id,
828 subnet_id)
829 client.networks.delete_router(router_id)
830
831
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200832def add_router_interface(routers):
833 for router in routers:
834 client = client_for_user(router['owner'])
835 router_id = _get_resource_by_name(client.networks,
836 'routers', router['name'])['id']
837
838 for subnet in router['subnet']:
839 subnet_id = _get_resource_by_name(client.networks,
840 'subnets', subnet)['id']
841 # connect routers to their subnets
842 client.networks.add_router_interface_with_subnet_id(router_id,
843 subnet_id)
nayna-patel1dfbedb2015-08-04 11:07:56 +0000844 # connect routers to external network if set to "gateway"
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200845 if router['gateway']:
846 if CONF.network.public_network_id:
847 ext_net = CONF.network.public_network_id
848 client.networks._update_router(
849 router_id, set_enable_snat=True,
850 external_gateway_info={"network_id": ext_net})
851 else:
852 raise ValueError('public_network_id is not configured.')
853
854
855#######################
856#
Sean Dague655e0af2014-05-29 09:00:22 -0400857# SERVERS
858#
859#######################
860
861def _get_server_by_name(client, name):
David Kranzae99b9a2015-02-16 13:37:01 -0500862 body = client.servers.list_servers()
Sean Dague655e0af2014-05-29 09:00:22 -0400863 for server in body['servers']:
864 if name == server['name']:
865 return server
866 return None
867
868
Sean Dague655e0af2014-05-29 09:00:22 -0400869def _get_flavor_by_name(client, name):
ghanshyam19973be2015-08-18 15:46:42 +0900870 body = client.flavors.list_flavors()['flavors']
Sean Dague655e0af2014-05-29 09:00:22 -0400871 for flavor in body:
872 if name == flavor['name']:
873 return flavor
874 return None
875
876
877def create_servers(servers):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000878 if not servers:
879 return
Joe Gordona18d6862014-07-24 22:55:46 +0000880 LOG.info("Creating servers")
Sean Dague655e0af2014-05-29 09:00:22 -0400881 for server in servers:
882 client = client_for_user(server['owner'])
883
884 if _get_server_by_name(client, server['name']):
Joe Gordona18d6862014-07-24 22:55:46 +0000885 LOG.info("Server '%s' already exists" % server['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400886 continue
887
888 image_id = _get_image_by_name(client, server['image'])['id']
889 flavor_id = _get_flavor_by_name(client, server['flavor'])['id']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200890 # validate neutron is enabled and ironic disabled
891 kwargs = dict()
892 if (CONF.service_available.neutron and
893 not CONF.baremetal.driver_enabled and server.get('networks')):
894 get_net_id = lambda x: (_get_resource_by_name(
895 client.networks, 'networks', x)['id'])
896 kwargs['networks'] = [{'uuid': get_net_id(network)}
897 for network in server['networks']]
David Kranz0fb14292015-02-11 15:55:20 -0500898 body = client.servers.create_server(
Ken'ichi Ohmichif2d436e2015-09-03 01:13:16 +0000899 name=server['name'], imageRef=image_id, flavorRef=flavor_id,
900 **kwargs)['server']
Joe Gordon10f260b2014-07-24 23:27:19 +0000901 server_id = body['id']
902 client.servers.wait_for_server_status(server_id, 'ACTIVE')
nayna-patel1dfbedb2015-08-04 11:07:56 +0000903 # create security group(s) after server spawning
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200904 for secgroup in server['secgroups']:
Ken'ichi Ohmichie6349f32015-12-09 06:47:54 +0000905 client.servers.add_security_group(server_id, name=secgroup)
Emilien Macchic3e3e292015-03-11 17:42:08 -0400906 if CONF.compute.use_floatingip_for_ssh:
Joe H. Rahme9350a102015-03-29 17:39:20 +0200907 floating_ip_pool = server.get('floating_ip_pool')
908 floating_ip = client.floating_ips.create_floating_ip(
ghanshyam9a3a9a22015-08-18 17:03:55 +0900909 pool_name=floating_ip_pool)['floating_ip']
Emilien Macchic3e3e292015-03-11 17:42:08 -0400910 client.floating_ips.associate_floating_ip_to_server(
911 floating_ip['ip'], server_id)
Sean Dague655e0af2014-05-29 09:00:22 -0400912
913
Joe Gordondb63b1c2014-07-24 23:21:21 +0000914def destroy_servers(servers):
915 if not servers:
916 return
917 LOG.info("Destroying servers")
918 for server in servers:
919 client = client_for_user(server['owner'])
920
Emilien Macchidc5bae22015-03-16 08:49:02 -0400921 response = _get_server_by_name(client, server['name'])
922 if not response:
Joe Gordondb63b1c2014-07-24 23:21:21 +0000923 LOG.info("Server '%s' does not exist" % server['name'])
924 continue
925
Emilien Macchidc5bae22015-03-16 08:49:02 -0400926 # TODO(EmilienM): disassociate floating IP from server and release it.
927 client.servers.delete_server(response['id'])
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000928 waiters.wait_for_server_termination(client.servers, response['id'],
929 ignore_error=True)
Joe Gordondb63b1c2014-07-24 23:21:21 +0000930
931
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200932def create_secgroups(secgroups):
933 LOG.info("Creating security groups")
934 for secgroup in secgroups:
935 client = client_for_user(secgroup['owner'])
936
937 # only create a security group if the name isn't here
938 # i.e. a security group may be used by another server
939 # only create a router if the name isn't here
ghanshyamb610b772015-08-24 17:29:38 +0900940 body = client.secgroups.list_security_groups()['security_groups']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200941 if any(item['name'] == secgroup['name'] for item in body):
942 LOG.warning("Security group '%s' already exists" %
943 secgroup['name'])
944 continue
945
David Kranz9964b4e2015-02-06 15:45:29 -0500946 body = client.secgroups.create_security_group(
ghanshyamb610b772015-08-24 17:29:38 +0900947 name=secgroup['name'],
948 description=secgroup['description'])['security_group']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200949 secgroup_id = body['id']
950 # for each security group, create the rules
951 for rule in secgroup['rules']:
952 ip_proto, from_port, to_port, cidr = rule.split()
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +0000953 client.secrules.create_security_group_rule(
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000954 parent_group_id=secgroup_id, ip_protocol=ip_proto,
955 from_port=from_port, to_port=to_port, cidr=cidr)
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200956
957
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100958def destroy_secgroups(secgroups):
959 LOG.info("Destroying security groups")
960 for secgroup in secgroups:
961 client = client_for_user(secgroup['owner'])
962 sg_id = _get_resource_by_name(client.secgroups,
963 'security_groups',
964 secgroup['name'])
965 # sg rules are deleted automatically
966 client.secgroups.delete_security_group(sg_id['id'])
967
968
Sean Dague655e0af2014-05-29 09:00:22 -0400969#######################
970#
Emilien Macchi626b4f82014-06-15 21:44:29 +0200971# VOLUMES
972#
973#######################
974
975def _get_volume_by_name(client, name):
John Warren6177c9e2015-08-19 20:00:17 +0000976 body = client.volumes.list_volumes()['volumes']
Emilien Macchid18fec12014-09-15 14:32:54 -0400977 for volume in body:
978 if name == volume['display_name']:
Emilien Macchi626b4f82014-06-15 21:44:29 +0200979 return volume
980 return None
981
982
983def create_volumes(volumes):
Chris Dent51e76de2014-10-01 12:07:14 +0100984 if not volumes:
985 return
986 LOG.info("Creating volumes")
Emilien Macchi626b4f82014-06-15 21:44:29 +0200987 for volume in volumes:
988 client = client_for_user(volume['owner'])
989
990 # only create a volume if the name isn't here
Emilien Macchid18fec12014-09-15 14:32:54 -0400991 if _get_volume_by_name(client, volume['name']):
992 LOG.info("volume '%s' already exists" % volume['name'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200993 continue
994
Emilien Macchid18fec12014-09-15 14:32:54 -0400995 size = volume['gb']
996 v_name = volume['name']
Joseph Lanoux6809bab2014-12-18 14:57:18 +0000997 body = client.volumes.create_volume(size=size,
John Warren6177c9e2015-08-19 20:00:17 +0000998 display_name=v_name)['volume']
Emilien Macchid18fec12014-09-15 14:32:54 -0400999 client.volumes.wait_for_volume_status(body['id'], 'available')
Emilien Macchi626b4f82014-06-15 21:44:29 +02001000
1001
Emilien Macchibb71e072014-07-05 19:18:52 +02001002def destroy_volumes(volumes):
1003 for volume in volumes:
1004 client = client_for_user(volume['owner'])
1005 volume_id = _get_volume_by_name(client, volume['name'])['id']
Emilien Macchi5ebc27b2014-09-15 14:30:35 -04001006 client.volumes.detach_volume(volume_id)
1007 client.volumes.delete_volume(volume_id)
Emilien Macchibb71e072014-07-05 19:18:52 +02001008
1009
Emilien Macchi626b4f82014-06-15 21:44:29 +02001010def attach_volumes(volumes):
1011 for volume in volumes:
1012 client = client_for_user(volume['owner'])
Emilien Macchi626b4f82014-06-15 21:44:29 +02001013 server_id = _get_server_by_name(client, volume['server'])['id']
Emilien Macchid18fec12014-09-15 14:32:54 -04001014 volume_id = _get_volume_by_name(client, volume['name'])['id']
1015 device = volume['device']
1016 client.volumes.attach_volume(volume_id, server_id, device)
Emilien Macchi626b4f82014-06-15 21:44:29 +02001017
1018
1019#######################
1020#
Sean Dague655e0af2014-05-29 09:00:22 -04001021# MAIN LOGIC
1022#
1023#######################
1024
1025def create_resources():
1026 LOG.info("Creating Resources")
1027 # first create keystone level resources, and we need to be admin
nayna-patel1dfbedb2015-08-04 11:07:56 +00001028 # for this.
Sean Dague655e0af2014-05-29 09:00:22 -04001029 create_tenants(RES['tenants'])
1030 create_users(RES['users'])
1031 collect_users(RES['users'])
1032
1033 # next create resources in a well known order
1034 create_objects(RES['objects'])
1035 create_images(RES['images'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +02001036
1037 # validate neutron is enabled and ironic is disabled
1038 if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
1039 create_networks(RES['networks'])
1040 create_subnets(RES['subnets'])
1041 create_routers(RES['routers'])
1042 add_router_interface(RES['routers'])
1043
1044 create_secgroups(RES['secgroups'])
Emilien Macchid18fec12014-09-15 14:32:54 -04001045 create_volumes(RES['volumes'])
Joe H. Rahmec96129b2015-03-30 11:23:31 +02001046
1047 # Only attempt attaching the volumes if servers are defined in the
nayna-patel1dfbedb2015-08-04 11:07:56 +00001048 # resource file
Joe H. Rahmec96129b2015-03-30 11:23:31 +02001049 if 'servers' in RES:
1050 create_servers(RES['servers'])
1051 attach_volumes(RES['volumes'])
Sean Dague655e0af2014-05-29 09:00:22 -04001052
1053
Joe Gordondb63b1c2014-07-24 23:21:21 +00001054def destroy_resources():
1055 LOG.info("Destroying Resources")
1056 # Destroy in inverse order of create
Joe Gordondb63b1c2014-07-24 23:21:21 +00001057 destroy_servers(RES['servers'])
Joe Gordon6f0426c2014-07-25 01:10:28 +00001058 destroy_images(RES['images'])
Emilien Macchibb71e072014-07-05 19:18:52 +02001059 destroy_objects(RES['objects'])
Emilien Macchibb71e072014-07-05 19:18:52 +02001060 destroy_volumes(RES['volumes'])
Jakub Libosvar3791ac92014-11-11 13:23:44 +01001061 if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
1062 destroy_routers(RES['routers'])
1063 destroy_subnets(RES['subnets'])
1064 destroy_networks(RES['networks'])
1065 destroy_secgroups(RES['secgroups'])
Emilien Macchibb71e072014-07-05 19:18:52 +02001066 destroy_users(RES['users'])
1067 destroy_tenants(RES['tenants'])
Joe Gordon6f0426c2014-07-25 01:10:28 +00001068 LOG.warn("Destroy mode incomplete")
1069
Joe Gordondb63b1c2014-07-24 23:21:21 +00001070
Sean Dague655e0af2014-05-29 09:00:22 -04001071def get_options():
1072 global OPTS
1073 parser = argparse.ArgumentParser(
1074 description='Create and validate a fixed set of OpenStack resources')
1075 parser.add_argument('-m', '--mode',
1076 metavar='<create|check|destroy>',
1077 required=True,
1078 help=('One of (create, check, destroy)'))
1079 parser.add_argument('-r', '--resources',
1080 required=True,
1081 metavar='resourcefile.yaml',
1082 help='Resources definition yaml file')
Joe Gordon28a84ae2014-07-17 15:38:28 +00001083
Sean Dague319b37a2014-07-11 07:28:11 -04001084 parser.add_argument(
1085 '-d', '--devstack-base',
1086 required=True,
1087 metavar='/opt/stack/old',
1088 help='Devstack base directory for retrieving artifacts')
Joe Gordon28a84ae2014-07-17 15:38:28 +00001089 parser.add_argument(
1090 '-c', '--config-file',
1091 metavar='/etc/tempest.conf',
1092 help='path to javelin2(tempest) config file')
1093
Sean Dague655e0af2014-05-29 09:00:22 -04001094 # auth bits, letting us also just source the devstack openrc
1095 parser.add_argument('--os-username',
1096 metavar='<auth-user-name>',
1097 default=os.environ.get('OS_USERNAME'),
1098 help=('Defaults to env[OS_USERNAME].'))
1099 parser.add_argument('--os-password',
1100 metavar='<auth-password>',
1101 default=os.environ.get('OS_PASSWORD'),
1102 help=('Defaults to env[OS_PASSWORD].'))
1103 parser.add_argument('--os-tenant-name',
1104 metavar='<auth-tenant-name>',
1105 default=os.environ.get('OS_TENANT_NAME'),
1106 help=('Defaults to env[OS_TENANT_NAME].'))
1107
1108 OPTS = parser.parse_args()
1109 if OPTS.mode not in ('create', 'check', 'destroy'):
1110 print("ERROR: Unknown mode -m %s\n" % OPTS.mode)
1111 parser.print_help()
1112 sys.exit(1)
Joe Gordon28a84ae2014-07-17 15:38:28 +00001113 if OPTS.config_file:
1114 config.CONF.set_config_path(OPTS.config_file)
Sean Dague655e0af2014-05-29 09:00:22 -04001115
1116
Joe Gordon915eb8e2014-07-17 11:25:46 +02001117def setup_logging():
Sean Dague655e0af2014-05-29 09:00:22 -04001118 global LOG
Doug Hellmann583ce2c2015-03-11 14:55:46 +00001119 logging.setup(CONF, __name__)
Sean Dague655e0af2014-05-29 09:00:22 -04001120 LOG = logging.getLogger(__name__)
Sean Dague655e0af2014-05-29 09:00:22 -04001121
1122
1123def main():
1124 global RES
1125 get_options()
1126 setup_logging()
Chris Dent51e76de2014-10-01 12:07:14 +01001127 RES.update(load_resources(OPTS.resources))
Sean Dague655e0af2014-05-29 09:00:22 -04001128
1129 if OPTS.mode == 'create':
1130 create_resources()
Joe Gordon1a097002014-07-24 23:44:08 +00001131 # Make sure the resources we just created actually work
1132 checker = JavelinCheck(USERS, RES)
1133 checker.check()
Sean Dague655e0af2014-05-29 09:00:22 -04001134 elif OPTS.mode == 'check':
1135 collect_users(RES['users'])
1136 checker = JavelinCheck(USERS, RES)
1137 checker.check()
1138 elif OPTS.mode == 'destroy':
Joe Gordondb63b1c2014-07-24 23:21:21 +00001139 collect_users(RES['users'])
1140 destroy_resources()
Sean Dague655e0af2014-05-29 09:00:22 -04001141 else:
1142 LOG.error('Unknown mode %s' % OPTS.mode)
1143 return 1
Joe Gordon246353a2014-07-18 00:10:28 +02001144 LOG.info('javelin2 successfully finished')
Sean Dague655e0af2014-05-29 09:00:22 -04001145 return 0
1146
1147if __name__ == "__main__":
1148 sys.exit(main())