blob: 95be89efc25ff8c63a6bc337c33c947e4c59ec29 [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
Matthew Treinish96e9e882014-06-09 18:37:19 -0400118import yaml
Sean Dague655e0af2014-05-29 09:00:22 -0400119
Ken'ichi Ohmichi6ea3f982015-11-09 12:41:13 +0000120from tempest.common import identity
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000121from tempest.common import waiters
Joe Gordon28a84ae2014-07-17 15:38:28 +0000122from tempest import config
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -0500123from tempest.lib import auth
124from tempest.lib import exceptions as lib_exc
125from tempest.lib.services.compute import flavors_client
126from tempest.lib.services.compute import floating_ips_client
127from tempest.lib.services.compute import security_group_rules_client
128from tempest.lib.services.compute import security_groups_client
129from tempest.lib.services.compute import servers_client
130from tempest.lib.services.network import subnets_client
Jamie Lennoxc429e6a2015-02-24 10:42:42 +1100131from tempest.services.identity.v2.json import identity_client
Daniel Mellado6b16b922015-12-07 12:43:08 +0000132from tempest.services.identity.v2.json import roles_client
Daniel Melladob04da902015-11-20 17:43:12 +0100133from tempest.services.identity.v2.json import tenants_client
Daniel Mellado82c83a52015-12-09 15:16:49 +0000134from tempest.services.identity.v2.json import users_client
Ken'ichi Ohmichi69dcf442015-11-30 11:48:01 +0000135from tempest.services.image.v2.json import images_client
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200136from tempest.services.network.json import network_client
Sean Dague655e0af2014-05-29 09:00:22 -0400137from tempest.services.object_storage import container_client
138from tempest.services.object_storage import object_client
liu-sheng67b730e2015-07-16 15:19:33 +0800139from tempest.services.telemetry.json import alarming_client
Chris Dent878f3782014-06-30 17:04:15 +0100140from tempest.services.telemetry.json import telemetry_client
Yaroslav Lobankovdb4a2e12015-11-28 20:04:54 +0300141from tempest.services.volume.v1.json import volumes_client
Sean Dague655e0af2014-05-29 09:00:22 -0400142
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200143CONF = config.CONF
Sean Dague655e0af2014-05-29 09:00:22 -0400144OPTS = {}
145USERS = {}
Chris Dent51e76de2014-10-01 12:07:14 +0100146RES = collections.defaultdict(list)
Sean Dague655e0af2014-05-29 09:00:22 -0400147
148LOG = None
149
Chris Dent878f3782014-06-30 17:04:15 +0100150JAVELIN_START = datetime.datetime.utcnow()
151
Sean Dague655e0af2014-05-29 09:00:22 -0400152
153class OSClient(object):
154 _creds = None
155 identity = None
156 servers = None
157
158 def __init__(self, user, pw, tenant):
Ken'ichi Ohmichi4771cbc2015-01-19 23:45:23 +0000159 default_params = {
160 'disable_ssl_certificate_validation':
161 CONF.identity.disable_ssl_certificate_validation,
162 'ca_certs': CONF.identity.ca_certificates_file,
163 'trace_requests': CONF.debug.trace_requests
164 }
Ken'ichi Ohmichid5dba1c2015-01-23 02:23:22 +0000165 default_params_with_timeout_values = {
166 'build_interval': CONF.compute.build_interval,
167 'build_timeout': CONF.compute.build_timeout
168 }
169 default_params_with_timeout_values.update(default_params)
170
Ken'ichi Ohmichi4771cbc2015-01-19 23:45:23 +0000171 compute_params = {
172 'service': CONF.compute.catalog_type,
173 'region': CONF.compute.region or CONF.identity.region,
174 'endpoint_type': CONF.compute.endpoint_type,
175 'build_interval': CONF.compute.build_interval,
176 'build_timeout': CONF.compute.build_timeout
177 }
178 compute_params.update(default_params)
179
Ken'ichi Ohmichi564b2ad2015-01-22 02:08:59 +0000180 object_storage_params = {
181 'service': CONF.object_storage.catalog_type,
182 'region': CONF.object_storage.region or CONF.identity.region,
183 'endpoint_type': CONF.object_storage.endpoint_type
184 }
185 object_storage_params.update(default_params)
186
andreafb8a52282015-03-19 22:21:54 +0000187 _creds = auth.KeystoneV2Credentials(
Sean Dague655e0af2014-05-29 09:00:22 -0400188 username=user,
189 password=pw,
190 tenant_name=tenant)
Andrea Frittoli90012352015-02-25 21:58:02 +0000191 auth_provider_params = {
192 'disable_ssl_certificate_validation':
193 CONF.identity.disable_ssl_certificate_validation,
194 'ca_certs': CONF.identity.ca_certificates_file,
195 'trace_requests': CONF.debug.trace_requests
196 }
andreafb8a52282015-03-19 22:21:54 +0000197 _auth = auth.KeystoneV2AuthProvider(
Andrea Frittoli90012352015-02-25 21:58:02 +0000198 _creds, CONF.identity.uri, **auth_provider_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000199 self.identity = identity_client.IdentityClient(
ghanshyamd26b5cd2015-02-09 14:48:58 +0900200 _auth,
201 CONF.identity.catalog_type,
202 CONF.identity.region,
203 endpoint_type='adminURL',
204 **default_params_with_timeout_values)
Daniel Melladob04da902015-11-20 17:43:12 +0100205 self.tenants = tenants_client.TenantsClient(
206 _auth,
207 CONF.identity.catalog_type,
208 CONF.identity.region,
209 endpoint_type='adminURL',
210 **default_params_with_timeout_values)
Daniel Mellado6b16b922015-12-07 12:43:08 +0000211 self.roles = roles_client.RolesClient(
212 _auth,
213 CONF.identity.catalog_type,
214 CONF.identity.region,
215 endpoint_type='adminURL',
216 **default_params_with_timeout_values)
Daniel Mellado82c83a52015-12-09 15:16:49 +0000217 self.users = users_client.UsersClient(
218 _auth,
219 CONF.identity.catalog_type,
220 CONF.identity.region,
221 endpoint_type='adminURL',
222 **default_params_with_timeout_values)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000223 self.servers = servers_client.ServersClient(_auth,
224 **compute_params)
225 self.flavors = flavors_client.FlavorsClient(_auth,
226 **compute_params)
227 self.floating_ips = floating_ips_client.FloatingIPsClient(
Emilien Macchic3e3e292015-03-11 17:42:08 -0400228 _auth, **compute_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000229 self.secgroups = security_groups_client.SecurityGroupsClient(
Ken'ichi Ohmichi4771cbc2015-01-19 23:45:23 +0000230 _auth, **compute_params)
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +0000231 self.secrules = security_group_rules_client.SecurityGroupRulesClient(
232 _auth, **compute_params)
Ken'ichi Ohmichi564b2ad2015-01-22 02:08:59 +0000233 self.objects = object_client.ObjectClient(_auth,
234 **object_storage_params)
235 self.containers = container_client.ContainerClient(
236 _auth, **object_storage_params)
Ken'ichi Ohmichi69dcf442015-11-30 11:48:01 +0000237 self.images = images_client.ImagesClientV2(
Masayuki Igawabc7e1892015-03-03 11:46:48 +0900238 _auth,
239 CONF.image.catalog_type,
240 CONF.image.region or CONF.identity.region,
241 endpoint_type=CONF.image.endpoint_type,
242 build_interval=CONF.image.build_interval,
243 build_timeout=CONF.image.build_timeout,
244 **default_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000245 self.telemetry = telemetry_client.TelemetryClient(
Ken'ichi Ohmichid5dba1c2015-01-23 02:23:22 +0000246 _auth,
247 CONF.telemetry.catalog_type,
248 CONF.identity.region,
249 endpoint_type=CONF.telemetry.endpoint_type,
250 **default_params_with_timeout_values)
liu-sheng67b730e2015-07-16 15:19:33 +0800251 self.alarming = alarming_client.AlarmingClient(
252 _auth,
253 CONF.alarm.catalog_type,
254 CONF.identity.region,
255 endpoint_type=CONF.alarm.endpoint_type,
256 **default_params_with_timeout_values)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000257 self.volumes = volumes_client.VolumesClient(
Ken'ichi Ohmichif85e9bd2015-01-27 12:51:47 +0000258 _auth,
259 CONF.volume.catalog_type,
260 CONF.volume.region or CONF.identity.region,
261 endpoint_type=CONF.volume.endpoint_type,
262 build_interval=CONF.volume.build_interval,
263 build_timeout=CONF.volume.build_timeout,
264 **default_params)
Ken'ichi Ohmichia6287072015-07-02 02:43:15 +0000265 self.networks = network_client.NetworkClient(
Ken'ichi Ohmichia182e862015-01-21 01:16:37 +0000266 _auth,
267 CONF.network.catalog_type,
268 CONF.network.region or CONF.identity.region,
269 endpoint_type=CONF.network.endpoint_type,
270 build_interval=CONF.network.build_interval,
271 build_timeout=CONF.network.build_timeout,
272 **default_params)
John Warren3961acd2015-10-02 14:38:53 -0400273 self.subnets = subnets_client.SubnetsClient(
274 _auth,
275 CONF.network.catalog_type,
276 CONF.network.region or CONF.identity.region,
277 endpoint_type=CONF.network.endpoint_type,
278 build_interval=CONF.network.build_interval,
279 build_timeout=CONF.network.build_timeout,
280 **default_params)
Sean Dague655e0af2014-05-29 09:00:22 -0400281
282
283def load_resources(fname):
Joe H. Rahme02736732015-01-27 18:33:09 +0100284 """Load the expected resources from a yaml file."""
Sean Dague655e0af2014-05-29 09:00:22 -0400285 return yaml.load(open(fname, 'r'))
286
287
288def keystone_admin():
289 return OSClient(OPTS.os_username, OPTS.os_password, OPTS.os_tenant_name)
290
291
292def client_for_user(name):
293 LOG.debug("Entering client_for_user")
294 if name in USERS:
295 user = USERS[name]
296 LOG.debug("Created client for user %s" % user)
297 return OSClient(user['name'], user['pass'], user['tenant'])
298 else:
299 LOG.error("%s not found in USERS: %s" % (name, USERS))
300
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200301
Sean Dague655e0af2014-05-29 09:00:22 -0400302###################
303#
304# TENANTS
305#
306###################
307
308
309def create_tenants(tenants):
310 """Create tenants from resource definition.
311
312 Don't create the tenants if they already exist.
313 """
314 admin = keystone_admin()
Daniel Melladob04da902015-11-20 17:43:12 +0100315 body = admin.tenants.list_tenants()['tenants']
Sean Dague655e0af2014-05-29 09:00:22 -0400316 existing = [x['name'] for x in body]
317 for tenant in tenants:
318 if tenant not in existing:
Daniel Melladob04da902015-11-20 17:43:12 +0100319 admin.tenants.create_tenant(tenant)['tenant']
Sean Dague655e0af2014-05-29 09:00:22 -0400320 else:
zhangguoqing6c096642016-01-04 06:17:21 +0000321 LOG.warning("Tenant '%s' already exists in this environment"
322 % tenant)
Sean Dague655e0af2014-05-29 09:00:22 -0400323
Emilien Macchibb71e072014-07-05 19:18:52 +0200324
325def destroy_tenants(tenants):
326 admin = keystone_admin()
327 for tenant in tenants:
Daniel Melladob04da902015-11-20 17:43:12 +0100328 tenant_id = identity.get_tenant_by_name(admin.tenant, tenant)['id']
329 admin.tenants.delete_tenant(tenant_id)
Emilien Macchibb71e072014-07-05 19:18:52 +0200330
Sean Dague655e0af2014-05-29 09:00:22 -0400331##############
332#
333# USERS
334#
335##############
336
337
338def _users_for_tenant(users, tenant):
339 u_for_t = []
340 for user in users:
341 for n in user:
342 if user[n]['tenant'] == tenant:
343 u_for_t.append(user[n])
344 return u_for_t
345
346
347def _tenants_from_users(users):
348 tenants = set()
349 for user in users:
350 for n in user:
351 tenants.add(user[n]['tenant'])
352 return tenants
353
354
Joe H. Rahme9c9fc4d2015-03-31 00:35:05 +0200355def _assign_swift_role(user, swift_role):
Sean Dague655e0af2014-05-29 09:00:22 -0400356 admin = keystone_admin()
Daniel Mellado6b16b922015-12-07 12:43:08 +0000357 roles = admin.roles.list_roles()
Joe H. Rahme9c9fc4d2015-03-31 00:35:05 +0200358 role = next(r for r in roles if r['name'] == swift_role)
Sean Dague655e0af2014-05-29 09:00:22 -0400359 LOG.debug(USERS[user])
360 try:
Daniel Mellado6b16b922015-12-07 12:43:08 +0000361 admin.roles.assign_user_role(
Sean Dague655e0af2014-05-29 09:00:22 -0400362 USERS[user]['tenant_id'],
363 USERS[user]['id'],
364 role['id'])
Masayuki Igawad9388762015-01-20 14:56:42 +0900365 except lib_exc.Conflict:
Sean Dague655e0af2014-05-29 09:00:22 -0400366 # don't care if it's already assigned
367 pass
368
369
370def create_users(users):
371 """Create tenants from resource definition.
372
373 Don't create the tenants if they already exist.
374 """
375 global USERS
376 LOG.info("Creating users")
377 admin = keystone_admin()
378 for u in users:
379 try:
Daniel Melladob04da902015-11-20 17:43:12 +0100380 tenant = identity.get_tenant_by_name(admin.tenants, u['tenant'])
Masayuki Igawabfa07602015-01-20 18:47:17 +0900381 except lib_exc.NotFound:
Sean Dague655e0af2014-05-29 09:00:22 -0400382 LOG.error("Tenant: %s - not found" % u['tenant'])
383 continue
384 try:
Daniel Mellado82c83a52015-12-09 15:16:49 +0000385 identity.get_user_by_username(admin.tenants,
Ken'ichi Ohmichid9fed312015-11-09 13:05:32 +0000386 tenant['id'], u['name'])
zhangguoqing6c096642016-01-04 06:17:21 +0000387 LOG.warning("User '%s' already exists in this environment"
388 % u['name'])
Masayuki Igawabfa07602015-01-20 18:47:17 +0900389 except lib_exc.NotFound:
Daniel Mellado82c83a52015-12-09 15:16:49 +0000390 admin.users.create_user(
Sean Dague655e0af2014-05-29 09:00:22 -0400391 u['name'], u['pass'], tenant['id'],
392 "%s@%s" % (u['name'], tenant['id']),
393 enabled=True)
394
395
Emilien Macchibb71e072014-07-05 19:18:52 +0200396def destroy_users(users):
397 admin = keystone_admin()
398 for user in users:
Daniel Melladob04da902015-11-20 17:43:12 +0100399 tenant_id = identity.get_tenant_by_name(admin.tenants,
Ken'ichi Ohmichi6ea3f982015-11-09 12:41:13 +0000400 user['tenant'])['id']
Daniel Mellado82c83a52015-12-09 15:16:49 +0000401 user_id = identity.get_user_by_username(admin.tenants,
Ken'ichi Ohmichid9fed312015-11-09 13:05:32 +0000402 tenant_id, user['name'])['id']
Daniel Mellado82c83a52015-12-09 15:16:49 +0000403 admin.users.delete_user(user_id)
Emilien Macchibb71e072014-07-05 19:18:52 +0200404
405
Sean Dague655e0af2014-05-29 09:00:22 -0400406def collect_users(users):
407 global USERS
Joe Gordon618c9fb2014-07-16 15:40:01 +0200408 LOG.info("Collecting users")
Sean Dague655e0af2014-05-29 09:00:22 -0400409 admin = keystone_admin()
410 for u in users:
Daniel Melladob04da902015-11-20 17:43:12 +0100411 tenant = identity.get_tenant_by_name(admin.tenants, u['tenant'])
Sean Dague655e0af2014-05-29 09:00:22 -0400412 u['tenant_id'] = tenant['id']
413 USERS[u['name']] = u
Daniel Mellado82c83a52015-12-09 15:16:49 +0000414 body = identity.get_user_by_username(admin.tenants,
Ken'ichi Ohmichid9fed312015-11-09 13:05:32 +0000415 tenant['id'], u['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400416 USERS[u['name']]['id'] = body['id']
417
418
419class JavelinCheck(unittest.TestCase):
420 def __init__(self, users, resources):
421 super(JavelinCheck, self).__init__()
422 self.users = users
423 self.res = resources
424
425 def runTest(self, *args):
426 pass
427
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200428 def _ping_ip(self, ip_addr, count, namespace=None):
429 if namespace is None:
430 ping_cmd = "ping -c1 " + ip_addr
431 else:
432 ping_cmd = "sudo ip netns exec %s ping -c1 %s" % (namespace,
433 ip_addr)
434 for current in range(count):
435 return_code = os.system(ping_cmd)
436 if return_code is 0:
437 break
438 self.assertNotEqual(current, count - 1,
439 "Server is not pingable at %s" % ip_addr)
440
Sean Dague655e0af2014-05-29 09:00:22 -0400441 def check(self):
442 self.check_users()
443 self.check_objects()
444 self.check_servers()
Emilien Macchid18fec12014-09-15 14:32:54 -0400445 self.check_volumes()
Chris Dent878f3782014-06-30 17:04:15 +0100446 self.check_telemetry()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200447 self.check_secgroups()
448
449 # validate neutron is enabled and ironic disabled:
450 # Tenant network isolation is not supported when using ironic.
451 # "admin" has set up a neutron flat network environment within a shared
452 # fixed network for all tenants to use.
453 # In this case, network/subnet/router creation can be skipped and the
454 # server booted the same as nova network.
455 if (CONF.service_available.neutron and
456 not CONF.baremetal.driver_enabled):
457 self.check_networking()
Sean Dague655e0af2014-05-29 09:00:22 -0400458
459 def check_users(self):
460 """Check that the users we expect to exist, do.
461
462 We don't use the resource list for this because we need to validate
463 that things like tenantId didn't drift across versions.
464 """
Joe Gordon618c9fb2014-07-16 15:40:01 +0200465 LOG.info("checking users")
Matthew Treinish71426682015-04-23 11:19:38 -0400466 for name, user in six.iteritems(self.users):
Sean Dague655e0af2014-05-29 09:00:22 -0400467 client = keystone_admin()
Daniel Mellado82c83a52015-12-09 15:16:49 +0000468 found = client.users.show_user(user['id'])['user']
Sean Dague655e0af2014-05-29 09:00:22 -0400469 self.assertEqual(found['name'], user['name'])
470 self.assertEqual(found['tenantId'], user['tenant_id'])
471
472 # also ensure we can auth with that user, and do something
473 # on the cloud. We don't care about the results except that it
474 # remains authorized.
475 client = client_for_user(user['name'])
David Kranzae99b9a2015-02-16 13:37:01 -0500476 client.servers.list_servers()
Sean Dague655e0af2014-05-29 09:00:22 -0400477
478 def check_objects(self):
479 """Check that the objects created are still there."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000480 if not self.res.get('objects'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000481 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200482 LOG.info("checking objects")
Sean Dague655e0af2014-05-29 09:00:22 -0400483 for obj in self.res['objects']:
484 client = client_for_user(obj['owner'])
485 r, contents = client.objects.get_object(
486 obj['container'], obj['name'])
487 source = _file_contents(obj['file'])
488 self.assertEqual(contents, source)
489
490 def check_servers(self):
491 """Check that the servers are still up and running."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000492 if not self.res.get('servers'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000493 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200494 LOG.info("checking servers")
Sean Dague655e0af2014-05-29 09:00:22 -0400495 for server in self.res['servers']:
496 client = client_for_user(server['owner'])
497 found = _get_server_by_name(client, server['name'])
498 self.assertIsNotNone(
499 found,
500 "Couldn't find expected server %s" % server['name'])
501
ghanshyam0f825252015-08-25 16:02:50 +0900502 found = client.servers.show_server(found['id'])['server']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200503 # validate neutron is enabled and ironic disabled:
504 if (CONF.service_available.neutron and
505 not CONF.baremetal.driver_enabled):
Emilien Macchic3e3e292015-03-11 17:42:08 -0400506 _floating_is_alive = False
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200507 for network_name, body in found['addresses'].items():
508 for addr in body:
509 ip = addr['addr']
lanoux283273b2015-12-04 03:01:54 -0800510 # Use floating IP, fixed IP or other type to
511 # reach the server.
Emilien Macchic3e3e292015-03-11 17:42:08 -0400512 # This is useful in multi-node environment.
lanoux283273b2015-12-04 03:01:54 -0800513 if CONF.validation.connect_method == 'floating':
Emilien Macchic3e3e292015-03-11 17:42:08 -0400514 if addr.get('OS-EXT-IPS:type',
515 'floating') == 'floating':
516 self._ping_ip(ip, 60)
517 _floating_is_alive = True
lanoux283273b2015-12-04 03:01:54 -0800518 elif CONF.validation.connect_method == 'fixed':
519 if addr.get('OS-EXT-IPS:type',
520 'fixed') == 'fixed':
521 namespace = _get_router_namespace(client,
522 network_name)
523 self._ping_ip(ip, 60, namespace)
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200524 else:
525 self._ping_ip(ip, 60)
lanoux283273b2015-12-04 03:01:54 -0800526 # If CONF.validation.connect_method is floating, validate
527 # that the floating IP is attached to the server and the
528 # the server is pingable.
529 if CONF.validation.connect_method == 'floating':
Emilien Macchic3e3e292015-03-11 17:42:08 -0400530 self.assertTrue(_floating_is_alive,
531 "Server %s has no floating IP." %
532 server['name'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200533 else:
534 addr = found['addresses']['private'][0]['addr']
535 self._ping_ip(addr, 60)
536
537 def check_secgroups(self):
Joe H. Rahme02736732015-01-27 18:33:09 +0100538 """Check that the security groups still exist."""
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200539 LOG.info("Checking security groups")
540 for secgroup in self.res['secgroups']:
541 client = client_for_user(secgroup['owner'])
542 found = _get_resource_by_name(client.secgroups, 'security_groups',
543 secgroup['name'])
544 self.assertIsNotNone(
545 found,
546 "Couldn't find expected secgroup %s" % secgroup['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400547
Chris Dent878f3782014-06-30 17:04:15 +0100548 def check_telemetry(self):
549 """Check that ceilometer provides a sane sample.
550
nayna-patel1dfbedb2015-08-04 11:07:56 +0000551 Confirm that there is more than one sample and that they have the
Chris Dent878f3782014-06-30 17:04:15 +0100552 expected metadata.
553
554 If in check mode confirm that the oldest sample available is from
555 before the upgrade.
556 """
Chris Dent0b76f2f2014-10-10 14:24:28 +0100557 if not self.res.get('telemetry'):
558 return
Chris Dent878f3782014-06-30 17:04:15 +0100559 LOG.info("checking telemetry")
560 for server in self.res['servers']:
561 client = client_for_user(server['owner'])
David Kranz20d06f42015-02-09 14:54:15 -0500562 body = client.telemetry.list_samples(
Chris Dent878f3782014-06-30 17:04:15 +0100563 'instance',
564 query=('metadata.display_name', 'eq', server['name'])
565 )
Chris Dent878f3782014-06-30 17:04:15 +0100566 self.assertTrue(len(body) >= 1, 'expecting at least one sample')
567 self._confirm_telemetry_sample(server, body[-1])
568
Emilien Macchi626b4f82014-06-15 21:44:29 +0200569 def check_volumes(self):
570 """Check that the volumes are still there and attached."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000571 if not self.res.get('volumes'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000572 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200573 LOG.info("checking volumes")
Emilien Macchi626b4f82014-06-15 21:44:29 +0200574 for volume in self.res['volumes']:
575 client = client_for_user(volume['owner'])
Emilien Macchid18fec12014-09-15 14:32:54 -0400576 vol_body = _get_volume_by_name(client, volume['name'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200577 self.assertIsNotNone(
Emilien Macchid18fec12014-09-15 14:32:54 -0400578 vol_body,
Emilien Macchi626b4f82014-06-15 21:44:29 +0200579 "Couldn't find expected volume %s" % volume['name'])
580
581 # Verify that a volume's attachment retrieved
582 server_id = _get_server_by_name(client, volume['server'])['id']
Emilien Macchid18fec12014-09-15 14:32:54 -0400583 attachment = client.volumes.get_attachment_from_volume(vol_body)
584 self.assertEqual(vol_body['id'], attachment['volume_id'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200585 self.assertEqual(server_id, attachment['server_id'])
586
Chris Dent878f3782014-06-30 17:04:15 +0100587 def _confirm_telemetry_sample(self, server, sample):
588 """Check this sample matches the expected resource metadata."""
589 # Confirm display_name
590 self.assertEqual(server['name'],
591 sample['resource_metadata']['display_name'])
592 # Confirm instance_type of flavor
593 flavor = sample['resource_metadata'].get(
594 'flavor.name',
595 sample['resource_metadata'].get('instance_type')
596 )
597 self.assertEqual(server['flavor'], flavor)
598 # Confirm the oldest sample was created before upgrade.
599 if OPTS.mode == 'check':
600 oldest_timestamp = timeutils.normalize_time(
601 timeutils.parse_isotime(sample['timestamp']))
602 self.assertTrue(
603 oldest_timestamp < JAVELIN_START,
604 'timestamp should come before start of second javelin run'
605 )
606
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200607 def check_networking(self):
608 """Check that the networks are still there."""
609 for res_type in ('networks', 'subnets', 'routers'):
610 for res in self.res[res_type]:
611 client = client_for_user(res['owner'])
612 found = _get_resource_by_name(client.networks, res_type,
613 res['name'])
614 self.assertIsNotNone(
615 found,
616 "Couldn't find expected resource %s" % res['name'])
617
Sean Dague655e0af2014-05-29 09:00:22 -0400618
619#######################
620#
621# OBJECTS
622#
623#######################
624
625
626def _file_contents(fname):
627 with open(fname, 'r') as f:
628 return f.read()
629
630
631def create_objects(objects):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000632 if not objects:
633 return
Sean Dague655e0af2014-05-29 09:00:22 -0400634 LOG.info("Creating objects")
635 for obj in objects:
636 LOG.debug("Object %s" % obj)
Joe H. Rahme9c9fc4d2015-03-31 00:35:05 +0200637 swift_role = obj.get('swift_role', 'Member')
638 _assign_swift_role(obj['owner'], swift_role)
Sean Dague655e0af2014-05-29 09:00:22 -0400639 client = client_for_user(obj['owner'])
640 client.containers.create_container(obj['container'])
641 client.objects.create_object(
642 obj['container'], obj['name'],
643 _file_contents(obj['file']))
644
Emilien Macchibb71e072014-07-05 19:18:52 +0200645
646def destroy_objects(objects):
647 for obj in objects:
648 client = client_for_user(obj['owner'])
649 r, body = client.objects.delete_object(obj['container'], obj['name'])
Emilien Macchid70f5102014-09-10 09:54:49 -0400650 if not (200 <= int(r['status']) < 299):
Emilien Macchibb71e072014-07-05 19:18:52 +0200651 raise ValueError("unable to destroy object: [%s] %s" % (r, body))
652
653
Sean Dague655e0af2014-05-29 09:00:22 -0400654#######################
655#
656# IMAGES
657#
658#######################
659
660
Sean Dague319b37a2014-07-11 07:28:11 -0400661def _resolve_image(image, imgtype):
662 name = image[imgtype]
663 fname = os.path.join(OPTS.devstack_base, image['imgdir'], name)
664 return name, fname
665
666
Joe Gordon6f0426c2014-07-25 01:10:28 +0000667def _get_image_by_name(client, name):
Ken'ichi Ohmichie3acc122015-05-22 00:32:54 +0000668 body = client.images.list_images()
Joe Gordon6f0426c2014-07-25 01:10:28 +0000669 for image in body:
670 if name == image['name']:
671 return image
672 return None
673
674
Sean Dague655e0af2014-05-29 09:00:22 -0400675def create_images(images):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000676 if not images:
677 return
Joe Gordona18d6862014-07-24 22:55:46 +0000678 LOG.info("Creating images")
Sean Dague655e0af2014-05-29 09:00:22 -0400679 for image in images:
680 client = client_for_user(image['owner'])
681
Joe H. Rahme4352d5d2015-03-09 17:41:18 +0100682 # DEPRECATED: 'format' was used for ami images
683 # Use 'disk_format' and 'container_format' instead
684 if 'format' in image:
685 LOG.warning("Deprecated: 'format' is deprecated for images "
686 "description. Please use 'disk_format' and 'container_"
687 "format' instead.")
688 image['disk_format'] = image['format']
689 image['container_format'] = image['format']
690
Sean Dague655e0af2014-05-29 09:00:22 -0400691 # only upload a new image if the name isn't there
Joe Gordon6f0426c2014-07-25 01:10:28 +0000692 if _get_image_by_name(client, image['name']):
Joe Gordona18d6862014-07-24 22:55:46 +0000693 LOG.info("Image '%s' already exists" % image['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400694 continue
695
696 # special handling for 3 part image
697 extras = {}
Joe H. Rahme4352d5d2015-03-09 17:41:18 +0100698 if image['disk_format'] == 'ami':
Sean Dague319b37a2014-07-11 07:28:11 -0400699 name, fname = _resolve_image(image, 'aki')
David Kranz34f18782015-01-06 13:43:55 -0500700 aki = client.images.create_image(
Sean Dague319b37a2014-07-11 07:28:11 -0400701 'javelin_' + name, 'aki', 'aki')
Ken'ichi Ohmichi66494e92015-06-08 04:28:10 +0000702 client.images.store_image_file(aki.get('id'), open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400703 extras['kernel_id'] = aki.get('id')
704
Sean Dague319b37a2014-07-11 07:28:11 -0400705 name, fname = _resolve_image(image, 'ari')
David Kranz34f18782015-01-06 13:43:55 -0500706 ari = client.images.create_image(
Sean Dague319b37a2014-07-11 07:28:11 -0400707 'javelin_' + name, 'ari', 'ari')
Ken'ichi Ohmichi66494e92015-06-08 04:28:10 +0000708 client.images.store_image_file(ari.get('id'), open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400709 extras['ramdisk_id'] = ari.get('id')
710
Sean Dague319b37a2014-07-11 07:28:11 -0400711 _, fname = _resolve_image(image, 'file')
David Kranz34f18782015-01-06 13:43:55 -0500712 body = client.images.create_image(
Joe H. Rahme4352d5d2015-03-09 17:41:18 +0100713 image['name'], image['container_format'],
714 image['disk_format'], **extras)
Sean Dague655e0af2014-05-29 09:00:22 -0400715 image_id = body.get('id')
Ken'ichi Ohmichi66494e92015-06-08 04:28:10 +0000716 client.images.store_image_file(image_id, open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400717
718
Joe Gordon6f0426c2014-07-25 01:10:28 +0000719def destroy_images(images):
720 if not images:
721 return
722 LOG.info("Destroying images")
723 for image in images:
724 client = client_for_user(image['owner'])
725
726 response = _get_image_by_name(client, image['name'])
727 if not response:
nayna-patel1dfbedb2015-08-04 11:07:56 +0000728 LOG.info("Image '%s' does not exist" % image['name'])
Joe Gordon6f0426c2014-07-25 01:10:28 +0000729 continue
730 client.images.delete_image(response['id'])
731
732
Sean Dague655e0af2014-05-29 09:00:22 -0400733#######################
734#
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200735# NETWORKS
736#
737#######################
738
739def _get_router_namespace(client, network):
740 network_id = _get_resource_by_name(client.networks,
741 'networks', network)['id']
David Kranz34e88122014-12-11 15:24:05 -0500742 n_body = client.networks.list_routers()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200743 for router in n_body['routers']:
744 router_id = router['id']
David Kranz34e88122014-12-11 15:24:05 -0500745 r_body = client.networks.list_router_interfaces(router_id)
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200746 for port in r_body['ports']:
747 if port['network_id'] == network_id:
748 return "qrouter-%s" % router_id
749
750
751def _get_resource_by_name(client, resource, name):
752 get_resources = getattr(client, 'list_%s' % resource)
753 if get_resources is None:
754 raise AttributeError("client doesn't have method list_%s" % resource)
David Kranz34e88122014-12-11 15:24:05 -0500755 # Until all tempest client methods are changed to return only one value,
756 # we cannot assume they all have the same signature so we need to discard
757 # the unused response first value it two values are being returned.
758 body = get_resources()
Brandon Palmecd2ec02016-02-25 09:38:36 -0600759 if isinstance(body, tuple):
David Kranz34e88122014-12-11 15:24:05 -0500760 body = body[1]
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200761 if isinstance(body, dict):
762 body = body[resource]
763 for res in body:
764 if name == res['name']:
765 return res
766 raise ValueError('%s not found in %s resources' % (name, resource))
767
768
769def create_networks(networks):
770 LOG.info("Creating networks")
771 for network in networks:
772 client = client_for_user(network['owner'])
773
774 # only create a network if the name isn't here
David Kranz34e88122014-12-11 15:24:05 -0500775 body = client.networks.list_networks()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200776 if any(item['name'] == network['name'] for item in body['networks']):
nayna-patel1dfbedb2015-08-04 11:07:56 +0000777 LOG.warning("Duplicated network name: %s" % network['name'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200778 continue
779
780 client.networks.create_network(name=network['name'])
781
782
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100783def destroy_networks(networks):
784 LOG.info("Destroying subnets")
785 for network in networks:
786 client = client_for_user(network['owner'])
787 network_id = _get_resource_by_name(client.networks, 'networks',
788 network['name'])['id']
789 client.networks.delete_network(network_id)
790
791
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200792def create_subnets(subnets):
793 LOG.info("Creating subnets")
794 for subnet in subnets:
795 client = client_for_user(subnet['owner'])
796
797 network = _get_resource_by_name(client.networks, 'networks',
798 subnet['network'])
799 ip_version = netaddr.IPNetwork(subnet['range']).version
800 # ensure we don't overlap with another subnet in the network
801 try:
802 client.networks.create_subnet(network_id=network['id'],
803 cidr=subnet['range'],
804 name=subnet['name'],
805 ip_version=ip_version)
Masayuki Igawa4b29e472015-02-16 10:41:54 +0900806 except lib_exc.BadRequest as e:
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200807 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
808 if not is_overlapping_cidr:
809 raise
810
811
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100812def destroy_subnets(subnets):
813 LOG.info("Destroying subnets")
814 for subnet in subnets:
815 client = client_for_user(subnet['owner'])
John Warren3961acd2015-10-02 14:38:53 -0400816 subnet_id = _get_resource_by_name(client.subnets,
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100817 'subnets', subnet['name'])['id']
John Warren3961acd2015-10-02 14:38:53 -0400818 client.subnets.delete_subnet(subnet_id)
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100819
820
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200821def create_routers(routers):
822 LOG.info("Creating routers")
823 for router in routers:
824 client = client_for_user(router['owner'])
825
826 # only create a router if the name isn't here
David Kranz34e88122014-12-11 15:24:05 -0500827 body = client.networks.list_routers()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200828 if any(item['name'] == router['name'] for item in body['routers']):
nayna-patel1dfbedb2015-08-04 11:07:56 +0000829 LOG.warning("Duplicated router name: %s" % router['name'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200830 continue
831
832 client.networks.create_router(router['name'])
833
834
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100835def destroy_routers(routers):
836 LOG.info("Destroying routers")
837 for router in routers:
838 client = client_for_user(router['owner'])
839 router_id = _get_resource_by_name(client.networks,
840 'routers', router['name'])['id']
841 for subnet in router['subnet']:
842 subnet_id = _get_resource_by_name(client.networks,
843 'subnets', subnet)['id']
piyush11078694aca952015-12-17 12:54:44 +0530844 client.networks.remove_router_interface(router_id,
845 subnet_id=subnet_id)
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100846 client.networks.delete_router(router_id)
847
848
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200849def add_router_interface(routers):
850 for router in routers:
851 client = client_for_user(router['owner'])
852 router_id = _get_resource_by_name(client.networks,
853 'routers', router['name'])['id']
854
855 for subnet in router['subnet']:
856 subnet_id = _get_resource_by_name(client.networks,
857 'subnets', subnet)['id']
858 # connect routers to their subnets
piyush11078694aca952015-12-17 12:54:44 +0530859 client.networks.add_router_interface(router_id,
860 subnet_id=subnet_id)
nayna-patel1dfbedb2015-08-04 11:07:56 +0000861 # connect routers to external network if set to "gateway"
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200862 if router['gateway']:
863 if CONF.network.public_network_id:
864 ext_net = CONF.network.public_network_id
865 client.networks._update_router(
866 router_id, set_enable_snat=True,
867 external_gateway_info={"network_id": ext_net})
868 else:
869 raise ValueError('public_network_id is not configured.')
870
871
872#######################
873#
Sean Dague655e0af2014-05-29 09:00:22 -0400874# SERVERS
875#
876#######################
877
878def _get_server_by_name(client, name):
David Kranzae99b9a2015-02-16 13:37:01 -0500879 body = client.servers.list_servers()
Sean Dague655e0af2014-05-29 09:00:22 -0400880 for server in body['servers']:
881 if name == server['name']:
882 return server
883 return None
884
885
Sean Dague655e0af2014-05-29 09:00:22 -0400886def _get_flavor_by_name(client, name):
ghanshyam19973be2015-08-18 15:46:42 +0900887 body = client.flavors.list_flavors()['flavors']
Sean Dague655e0af2014-05-29 09:00:22 -0400888 for flavor in body:
889 if name == flavor['name']:
890 return flavor
891 return None
892
893
894def create_servers(servers):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000895 if not servers:
896 return
Joe Gordona18d6862014-07-24 22:55:46 +0000897 LOG.info("Creating servers")
Sean Dague655e0af2014-05-29 09:00:22 -0400898 for server in servers:
899 client = client_for_user(server['owner'])
900
901 if _get_server_by_name(client, server['name']):
Joe Gordona18d6862014-07-24 22:55:46 +0000902 LOG.info("Server '%s' already exists" % server['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400903 continue
904
905 image_id = _get_image_by_name(client, server['image'])['id']
906 flavor_id = _get_flavor_by_name(client, server['flavor'])['id']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200907 # validate neutron is enabled and ironic disabled
908 kwargs = dict()
909 if (CONF.service_available.neutron and
910 not CONF.baremetal.driver_enabled and server.get('networks')):
911 get_net_id = lambda x: (_get_resource_by_name(
912 client.networks, 'networks', x)['id'])
913 kwargs['networks'] = [{'uuid': get_net_id(network)}
914 for network in server['networks']]
David Kranz0fb14292015-02-11 15:55:20 -0500915 body = client.servers.create_server(
Ken'ichi Ohmichif2d436e2015-09-03 01:13:16 +0000916 name=server['name'], imageRef=image_id, flavorRef=flavor_id,
917 **kwargs)['server']
Joe Gordon10f260b2014-07-24 23:27:19 +0000918 server_id = body['id']
919 client.servers.wait_for_server_status(server_id, 'ACTIVE')
nayna-patel1dfbedb2015-08-04 11:07:56 +0000920 # create security group(s) after server spawning
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200921 for secgroup in server['secgroups']:
Ken'ichi Ohmichie6349f32015-12-09 06:47:54 +0000922 client.servers.add_security_group(server_id, name=secgroup)
lanoux283273b2015-12-04 03:01:54 -0800923 if CONF.validation.connect_method == 'floating':
Joe H. Rahme9350a102015-03-29 17:39:20 +0200924 floating_ip_pool = server.get('floating_ip_pool')
925 floating_ip = client.floating_ips.create_floating_ip(
ghanshyam9a3a9a22015-08-18 17:03:55 +0900926 pool_name=floating_ip_pool)['floating_ip']
Emilien Macchic3e3e292015-03-11 17:42:08 -0400927 client.floating_ips.associate_floating_ip_to_server(
928 floating_ip['ip'], server_id)
Sean Dague655e0af2014-05-29 09:00:22 -0400929
930
Joe Gordondb63b1c2014-07-24 23:21:21 +0000931def destroy_servers(servers):
932 if not servers:
933 return
934 LOG.info("Destroying servers")
935 for server in servers:
936 client = client_for_user(server['owner'])
937
Emilien Macchidc5bae22015-03-16 08:49:02 -0400938 response = _get_server_by_name(client, server['name'])
939 if not response:
Joe Gordondb63b1c2014-07-24 23:21:21 +0000940 LOG.info("Server '%s' does not exist" % server['name'])
941 continue
942
Emilien Macchidc5bae22015-03-16 08:49:02 -0400943 # TODO(EmilienM): disassociate floating IP from server and release it.
944 client.servers.delete_server(response['id'])
Ken'ichi Ohmichie91a0c62015-08-13 02:09:16 +0000945 waiters.wait_for_server_termination(client.servers, response['id'],
946 ignore_error=True)
Joe Gordondb63b1c2014-07-24 23:21:21 +0000947
948
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200949def create_secgroups(secgroups):
950 LOG.info("Creating security groups")
951 for secgroup in secgroups:
952 client = client_for_user(secgroup['owner'])
953
954 # only create a security group if the name isn't here
955 # i.e. a security group may be used by another server
956 # only create a router if the name isn't here
ghanshyamb610b772015-08-24 17:29:38 +0900957 body = client.secgroups.list_security_groups()['security_groups']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200958 if any(item['name'] == secgroup['name'] for item in body):
959 LOG.warning("Security group '%s' already exists" %
960 secgroup['name'])
961 continue
962
David Kranz9964b4e2015-02-06 15:45:29 -0500963 body = client.secgroups.create_security_group(
ghanshyamb610b772015-08-24 17:29:38 +0900964 name=secgroup['name'],
965 description=secgroup['description'])['security_group']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200966 secgroup_id = body['id']
967 # for each security group, create the rules
968 for rule in secgroup['rules']:
969 ip_proto, from_port, to_port, cidr = rule.split()
Ken'ichi Ohmichi685cd172015-07-13 01:29:57 +0000970 client.secrules.create_security_group_rule(
Ken'ichi Ohmichieb7eeec2015-07-21 01:00:06 +0000971 parent_group_id=secgroup_id, ip_protocol=ip_proto,
972 from_port=from_port, to_port=to_port, cidr=cidr)
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200973
974
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100975def destroy_secgroups(secgroups):
976 LOG.info("Destroying security groups")
977 for secgroup in secgroups:
978 client = client_for_user(secgroup['owner'])
979 sg_id = _get_resource_by_name(client.secgroups,
980 'security_groups',
981 secgroup['name'])
982 # sg rules are deleted automatically
983 client.secgroups.delete_security_group(sg_id['id'])
984
985
Sean Dague655e0af2014-05-29 09:00:22 -0400986#######################
987#
Emilien Macchi626b4f82014-06-15 21:44:29 +0200988# VOLUMES
989#
990#######################
991
992def _get_volume_by_name(client, name):
John Warren6177c9e2015-08-19 20:00:17 +0000993 body = client.volumes.list_volumes()['volumes']
Emilien Macchid18fec12014-09-15 14:32:54 -0400994 for volume in body:
995 if name == volume['display_name']:
Emilien Macchi626b4f82014-06-15 21:44:29 +0200996 return volume
997 return None
998
999
1000def create_volumes(volumes):
Chris Dent51e76de2014-10-01 12:07:14 +01001001 if not volumes:
1002 return
1003 LOG.info("Creating volumes")
Emilien Macchi626b4f82014-06-15 21:44:29 +02001004 for volume in volumes:
1005 client = client_for_user(volume['owner'])
1006
1007 # only create a volume if the name isn't here
Emilien Macchid18fec12014-09-15 14:32:54 -04001008 if _get_volume_by_name(client, volume['name']):
1009 LOG.info("volume '%s' already exists" % volume['name'])
Emilien Macchi626b4f82014-06-15 21:44:29 +02001010 continue
1011
Emilien Macchid18fec12014-09-15 14:32:54 -04001012 size = volume['gb']
1013 v_name = volume['name']
Joseph Lanoux6809bab2014-12-18 14:57:18 +00001014 body = client.volumes.create_volume(size=size,
John Warren6177c9e2015-08-19 20:00:17 +00001015 display_name=v_name)['volume']
Emilien Macchid18fec12014-09-15 14:32:54 -04001016 client.volumes.wait_for_volume_status(body['id'], 'available')
Emilien Macchi626b4f82014-06-15 21:44:29 +02001017
1018
Emilien Macchibb71e072014-07-05 19:18:52 +02001019def destroy_volumes(volumes):
1020 for volume in volumes:
1021 client = client_for_user(volume['owner'])
1022 volume_id = _get_volume_by_name(client, volume['name'])['id']
Emilien Macchi5ebc27b2014-09-15 14:30:35 -04001023 client.volumes.detach_volume(volume_id)
1024 client.volumes.delete_volume(volume_id)
Emilien Macchibb71e072014-07-05 19:18:52 +02001025
1026
Emilien Macchi626b4f82014-06-15 21:44:29 +02001027def attach_volumes(volumes):
1028 for volume in volumes:
1029 client = client_for_user(volume['owner'])
Emilien Macchi626b4f82014-06-15 21:44:29 +02001030 server_id = _get_server_by_name(client, volume['server'])['id']
Emilien Macchid18fec12014-09-15 14:32:54 -04001031 volume_id = _get_volume_by_name(client, volume['name'])['id']
1032 device = volume['device']
Ghanshyam8fc0ed22015-12-18 10:25:14 +09001033 client.volumes.attach_volume(volume_id,
1034 instance_uuid=server_id,
1035 mountpoint=device)
Emilien Macchi626b4f82014-06-15 21:44:29 +02001036
1037
1038#######################
1039#
Sean Dague655e0af2014-05-29 09:00:22 -04001040# MAIN LOGIC
1041#
1042#######################
1043
1044def create_resources():
1045 LOG.info("Creating Resources")
1046 # first create keystone level resources, and we need to be admin
nayna-patel1dfbedb2015-08-04 11:07:56 +00001047 # for this.
Sean Dague655e0af2014-05-29 09:00:22 -04001048 create_tenants(RES['tenants'])
1049 create_users(RES['users'])
1050 collect_users(RES['users'])
1051
1052 # next create resources in a well known order
1053 create_objects(RES['objects'])
1054 create_images(RES['images'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +02001055
1056 # validate neutron is enabled and ironic is disabled
1057 if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
1058 create_networks(RES['networks'])
1059 create_subnets(RES['subnets'])
1060 create_routers(RES['routers'])
1061 add_router_interface(RES['routers'])
1062
1063 create_secgroups(RES['secgroups'])
Emilien Macchid18fec12014-09-15 14:32:54 -04001064 create_volumes(RES['volumes'])
Joe H. Rahmec96129b2015-03-30 11:23:31 +02001065
1066 # Only attempt attaching the volumes if servers are defined in the
nayna-patel1dfbedb2015-08-04 11:07:56 +00001067 # resource file
Joe H. Rahmec96129b2015-03-30 11:23:31 +02001068 if 'servers' in RES:
1069 create_servers(RES['servers'])
1070 attach_volumes(RES['volumes'])
Sean Dague655e0af2014-05-29 09:00:22 -04001071
1072
Joe Gordondb63b1c2014-07-24 23:21:21 +00001073def destroy_resources():
1074 LOG.info("Destroying Resources")
1075 # Destroy in inverse order of create
Joe Gordondb63b1c2014-07-24 23:21:21 +00001076 destroy_servers(RES['servers'])
Joe Gordon6f0426c2014-07-25 01:10:28 +00001077 destroy_images(RES['images'])
Emilien Macchibb71e072014-07-05 19:18:52 +02001078 destroy_objects(RES['objects'])
Emilien Macchibb71e072014-07-05 19:18:52 +02001079 destroy_volumes(RES['volumes'])
Jakub Libosvar3791ac92014-11-11 13:23:44 +01001080 if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
1081 destroy_routers(RES['routers'])
1082 destroy_subnets(RES['subnets'])
1083 destroy_networks(RES['networks'])
1084 destroy_secgroups(RES['secgroups'])
Emilien Macchibb71e072014-07-05 19:18:52 +02001085 destroy_users(RES['users'])
1086 destroy_tenants(RES['tenants'])
zhangguoqing6c096642016-01-04 06:17:21 +00001087 LOG.warning("Destroy mode incomplete")
Joe Gordon6f0426c2014-07-25 01:10:28 +00001088
Joe Gordondb63b1c2014-07-24 23:21:21 +00001089
Sean Dague655e0af2014-05-29 09:00:22 -04001090def get_options():
1091 global OPTS
1092 parser = argparse.ArgumentParser(
1093 description='Create and validate a fixed set of OpenStack resources')
1094 parser.add_argument('-m', '--mode',
1095 metavar='<create|check|destroy>',
1096 required=True,
1097 help=('One of (create, check, destroy)'))
1098 parser.add_argument('-r', '--resources',
1099 required=True,
1100 metavar='resourcefile.yaml',
1101 help='Resources definition yaml file')
Joe Gordon28a84ae2014-07-17 15:38:28 +00001102
Sean Dague319b37a2014-07-11 07:28:11 -04001103 parser.add_argument(
1104 '-d', '--devstack-base',
1105 required=True,
1106 metavar='/opt/stack/old',
1107 help='Devstack base directory for retrieving artifacts')
Joe Gordon28a84ae2014-07-17 15:38:28 +00001108 parser.add_argument(
1109 '-c', '--config-file',
1110 metavar='/etc/tempest.conf',
1111 help='path to javelin2(tempest) config file')
1112
Sean Dague655e0af2014-05-29 09:00:22 -04001113 # auth bits, letting us also just source the devstack openrc
1114 parser.add_argument('--os-username',
1115 metavar='<auth-user-name>',
1116 default=os.environ.get('OS_USERNAME'),
1117 help=('Defaults to env[OS_USERNAME].'))
1118 parser.add_argument('--os-password',
1119 metavar='<auth-password>',
1120 default=os.environ.get('OS_PASSWORD'),
1121 help=('Defaults to env[OS_PASSWORD].'))
1122 parser.add_argument('--os-tenant-name',
1123 metavar='<auth-tenant-name>',
1124 default=os.environ.get('OS_TENANT_NAME'),
1125 help=('Defaults to env[OS_TENANT_NAME].'))
1126
1127 OPTS = parser.parse_args()
1128 if OPTS.mode not in ('create', 'check', 'destroy'):
1129 print("ERROR: Unknown mode -m %s\n" % OPTS.mode)
1130 parser.print_help()
1131 sys.exit(1)
Joe Gordon28a84ae2014-07-17 15:38:28 +00001132 if OPTS.config_file:
1133 config.CONF.set_config_path(OPTS.config_file)
Sean Dague655e0af2014-05-29 09:00:22 -04001134
1135
Joe Gordon915eb8e2014-07-17 11:25:46 +02001136def setup_logging():
Sean Dague655e0af2014-05-29 09:00:22 -04001137 global LOG
Doug Hellmann583ce2c2015-03-11 14:55:46 +00001138 logging.setup(CONF, __name__)
Sean Dague655e0af2014-05-29 09:00:22 -04001139 LOG = logging.getLogger(__name__)
Sean Dague655e0af2014-05-29 09:00:22 -04001140
1141
1142def main():
Matthew Treinish0ee37442016-01-12 16:05:40 -05001143 print("Javelin is deprecated and will be removed from Tempest in the "
1144 "future.")
Sean Dague655e0af2014-05-29 09:00:22 -04001145 global RES
1146 get_options()
1147 setup_logging()
Chris Dent51e76de2014-10-01 12:07:14 +01001148 RES.update(load_resources(OPTS.resources))
Sean Dague655e0af2014-05-29 09:00:22 -04001149
1150 if OPTS.mode == 'create':
1151 create_resources()
Joe Gordon1a097002014-07-24 23:44:08 +00001152 # Make sure the resources we just created actually work
1153 checker = JavelinCheck(USERS, RES)
1154 checker.check()
Sean Dague655e0af2014-05-29 09:00:22 -04001155 elif OPTS.mode == 'check':
1156 collect_users(RES['users'])
1157 checker = JavelinCheck(USERS, RES)
1158 checker.check()
1159 elif OPTS.mode == 'destroy':
Joe Gordondb63b1c2014-07-24 23:21:21 +00001160 collect_users(RES['users'])
1161 destroy_resources()
Sean Dague655e0af2014-05-29 09:00:22 -04001162 else:
1163 LOG.error('Unknown mode %s' % OPTS.mode)
1164 return 1
Joe Gordon246353a2014-07-18 00:10:28 +02001165 LOG.info('javelin2 successfully finished')
Sean Dague655e0af2014-05-29 09:00:22 -04001166 return 0
1167
1168if __name__ == "__main__":
1169 sys.exit(main())