blob: 97aa62b33fa5c8bcdf94114975892dd2ac4fff6f [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
15"""Javelin makes resources that should survive an upgrade.
16
17Javelin is a tool for creating, verifying, and deleting a small set of
18resources in a declarative way.
19
20"""
21
Matthew Treinish96e9e882014-06-09 18:37:19 -040022import argparse
Chris Dent51e76de2014-10-01 12:07:14 +010023import collections
Chris Dent878f3782014-06-30 17:04:15 +010024import datetime
Sean Dague655e0af2014-05-29 09:00:22 -040025import os
26import sys
27import unittest
Sean Dague655e0af2014-05-29 09:00:22 -040028
Emilien Macchi7a2348b2014-06-16 07:32:11 +020029import netaddr
Matthew Treinish96e9e882014-06-09 18:37:19 -040030import yaml
Sean Dague655e0af2014-05-29 09:00:22 -040031
32import tempest.auth
Joe Gordon28a84ae2014-07-17 15:38:28 +000033from tempest import config
Sean Dague655e0af2014-05-29 09:00:22 -040034from tempest import exceptions
Joe Gordon915eb8e2014-07-17 11:25:46 +020035from tempest.openstack.common import log as logging
Chris Dent878f3782014-06-30 17:04:15 +010036from tempest.openstack.common import timeutils
Sean Dague655e0af2014-05-29 09:00:22 -040037from tempest.services.compute.json import flavors_client
Emilien Macchi7a2348b2014-06-16 07:32:11 +020038from tempest.services.compute.json import security_groups_client
Sean Dague655e0af2014-05-29 09:00:22 -040039from tempest.services.compute.json import servers_client
40from tempest.services.identity.json import identity_client
41from tempest.services.image.v2.json import image_client
Emilien Macchi7a2348b2014-06-16 07:32:11 +020042from tempest.services.network.json import network_client
Sean Dague655e0af2014-05-29 09:00:22 -040043from tempest.services.object_storage import container_client
44from tempest.services.object_storage import object_client
Chris Dent878f3782014-06-30 17:04:15 +010045from tempest.services.telemetry.json import telemetry_client
Emilien Macchi626b4f82014-06-15 21:44:29 +020046from tempest.services.volume.json import volumes_client
Sean Dague655e0af2014-05-29 09:00:22 -040047
Emilien Macchi7a2348b2014-06-16 07:32:11 +020048CONF = config.CONF
Sean Dague655e0af2014-05-29 09:00:22 -040049OPTS = {}
50USERS = {}
Chris Dent51e76de2014-10-01 12:07:14 +010051RES = collections.defaultdict(list)
Sean Dague655e0af2014-05-29 09:00:22 -040052
53LOG = None
54
Chris Dent878f3782014-06-30 17:04:15 +010055JAVELIN_START = datetime.datetime.utcnow()
56
Sean Dague655e0af2014-05-29 09:00:22 -040057
58class OSClient(object):
59 _creds = None
60 identity = None
61 servers = None
62
63 def __init__(self, user, pw, tenant):
64 _creds = tempest.auth.KeystoneV2Credentials(
65 username=user,
66 password=pw,
67 tenant_name=tenant)
68 _auth = tempest.auth.KeystoneV2AuthProvider(_creds)
69 self.identity = identity_client.IdentityClientJSON(_auth)
70 self.servers = servers_client.ServersClientJSON(_auth)
71 self.objects = object_client.ObjectClient(_auth)
72 self.containers = container_client.ContainerClient(_auth)
73 self.images = image_client.ImageClientV2JSON(_auth)
74 self.flavors = flavors_client.FlavorsClientJSON(_auth)
Chris Dent878f3782014-06-30 17:04:15 +010075 self.telemetry = telemetry_client.TelemetryClientJSON(_auth)
Emilien Macchi7a2348b2014-06-16 07:32:11 +020076 self.secgroups = security_groups_client.SecurityGroupsClientJSON(_auth)
Emilien Macchi626b4f82014-06-15 21:44:29 +020077 self.volumes = volumes_client.VolumesClientJSON(_auth)
Emilien Macchi7a2348b2014-06-16 07:32:11 +020078 self.networks = network_client.NetworkClientJSON(_auth)
Sean Dague655e0af2014-05-29 09:00:22 -040079
80
81def load_resources(fname):
82 """Load the expected resources from a yaml flie."""
83 return yaml.load(open(fname, 'r'))
84
85
86def keystone_admin():
87 return OSClient(OPTS.os_username, OPTS.os_password, OPTS.os_tenant_name)
88
89
90def client_for_user(name):
91 LOG.debug("Entering client_for_user")
92 if name in USERS:
93 user = USERS[name]
94 LOG.debug("Created client for user %s" % user)
95 return OSClient(user['name'], user['pass'], user['tenant'])
96 else:
97 LOG.error("%s not found in USERS: %s" % (name, USERS))
98
Emilien Macchi7a2348b2014-06-16 07:32:11 +020099
100def resp_ok(response):
101 return 200 >= int(response['status']) < 300
102
Sean Dague655e0af2014-05-29 09:00:22 -0400103###################
104#
105# TENANTS
106#
107###################
108
109
110def create_tenants(tenants):
111 """Create tenants from resource definition.
112
113 Don't create the tenants if they already exist.
114 """
115 admin = keystone_admin()
116 _, body = admin.identity.list_tenants()
117 existing = [x['name'] for x in body]
118 for tenant in tenants:
119 if tenant not in existing:
120 admin.identity.create_tenant(tenant)
121 else:
122 LOG.warn("Tenant '%s' already exists in this environment" % tenant)
123
Emilien Macchibb71e072014-07-05 19:18:52 +0200124
125def destroy_tenants(tenants):
126 admin = keystone_admin()
127 for tenant in tenants:
128 tenant_id = admin.identity.get_tenant_by_name(tenant)['id']
129 r, body = admin.identity.delete_tenant(tenant_id)
130
Sean Dague655e0af2014-05-29 09:00:22 -0400131##############
132#
133# USERS
134#
135##############
136
137
138def _users_for_tenant(users, tenant):
139 u_for_t = []
140 for user in users:
141 for n in user:
142 if user[n]['tenant'] == tenant:
143 u_for_t.append(user[n])
144 return u_for_t
145
146
147def _tenants_from_users(users):
148 tenants = set()
149 for user in users:
150 for n in user:
151 tenants.add(user[n]['tenant'])
152 return tenants
153
154
155def _assign_swift_role(user):
156 admin = keystone_admin()
157 resp, roles = admin.identity.list_roles()
158 role = next(r for r in roles if r['name'] == 'Member')
159 LOG.debug(USERS[user])
160 try:
161 admin.identity.assign_user_role(
162 USERS[user]['tenant_id'],
163 USERS[user]['id'],
164 role['id'])
165 except exceptions.Conflict:
166 # don't care if it's already assigned
167 pass
168
169
170def create_users(users):
171 """Create tenants from resource definition.
172
173 Don't create the tenants if they already exist.
174 """
175 global USERS
176 LOG.info("Creating users")
177 admin = keystone_admin()
178 for u in users:
179 try:
180 tenant = admin.identity.get_tenant_by_name(u['tenant'])
181 except exceptions.NotFound:
182 LOG.error("Tenant: %s - not found" % u['tenant'])
183 continue
184 try:
185 admin.identity.get_user_by_username(tenant['id'], u['name'])
186 LOG.warn("User '%s' already exists in this environment"
187 % u['name'])
188 except exceptions.NotFound:
189 admin.identity.create_user(
190 u['name'], u['pass'], tenant['id'],
191 "%s@%s" % (u['name'], tenant['id']),
192 enabled=True)
193
194
Emilien Macchibb71e072014-07-05 19:18:52 +0200195def destroy_users(users):
196 admin = keystone_admin()
197 for user in users:
Emilien Macchi436de862014-09-30 17:09:50 -0400198 tenant_id = admin.identity.get_tenant_by_name(user['tenant'])['id']
199 user_id = admin.identity.get_user_by_username(tenant_id,
200 user['name'])['id']
Emilien Macchibb71e072014-07-05 19:18:52 +0200201 r, body = admin.identity.delete_user(user_id)
202
203
Sean Dague655e0af2014-05-29 09:00:22 -0400204def collect_users(users):
205 global USERS
Joe Gordon618c9fb2014-07-16 15:40:01 +0200206 LOG.info("Collecting users")
Sean Dague655e0af2014-05-29 09:00:22 -0400207 admin = keystone_admin()
208 for u in users:
209 tenant = admin.identity.get_tenant_by_name(u['tenant'])
210 u['tenant_id'] = tenant['id']
211 USERS[u['name']] = u
212 body = admin.identity.get_user_by_username(tenant['id'], u['name'])
213 USERS[u['name']]['id'] = body['id']
214
215
216class JavelinCheck(unittest.TestCase):
217 def __init__(self, users, resources):
218 super(JavelinCheck, self).__init__()
219 self.users = users
220 self.res = resources
221
222 def runTest(self, *args):
223 pass
224
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200225 def _ping_ip(self, ip_addr, count, namespace=None):
226 if namespace is None:
227 ping_cmd = "ping -c1 " + ip_addr
228 else:
229 ping_cmd = "sudo ip netns exec %s ping -c1 %s" % (namespace,
230 ip_addr)
231 for current in range(count):
232 return_code = os.system(ping_cmd)
233 if return_code is 0:
234 break
235 self.assertNotEqual(current, count - 1,
236 "Server is not pingable at %s" % ip_addr)
237
Sean Dague655e0af2014-05-29 09:00:22 -0400238 def check(self):
239 self.check_users()
240 self.check_objects()
241 self.check_servers()
Emilien Macchid18fec12014-09-15 14:32:54 -0400242 self.check_volumes()
Chris Dent878f3782014-06-30 17:04:15 +0100243 self.check_telemetry()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200244 self.check_secgroups()
245
246 # validate neutron is enabled and ironic disabled:
247 # Tenant network isolation is not supported when using ironic.
248 # "admin" has set up a neutron flat network environment within a shared
249 # fixed network for all tenants to use.
250 # In this case, network/subnet/router creation can be skipped and the
251 # server booted the same as nova network.
252 if (CONF.service_available.neutron and
253 not CONF.baremetal.driver_enabled):
254 self.check_networking()
Sean Dague655e0af2014-05-29 09:00:22 -0400255
256 def check_users(self):
257 """Check that the users we expect to exist, do.
258
259 We don't use the resource list for this because we need to validate
260 that things like tenantId didn't drift across versions.
261 """
Joe Gordon618c9fb2014-07-16 15:40:01 +0200262 LOG.info("checking users")
Sean Dague655e0af2014-05-29 09:00:22 -0400263 for name, user in self.users.iteritems():
264 client = keystone_admin()
265 _, found = client.identity.get_user(user['id'])
266 self.assertEqual(found['name'], user['name'])
267 self.assertEqual(found['tenantId'], user['tenant_id'])
268
269 # also ensure we can auth with that user, and do something
270 # on the cloud. We don't care about the results except that it
271 # remains authorized.
272 client = client_for_user(user['name'])
273 resp, body = client.servers.list_servers()
274 self.assertEqual(resp['status'], '200')
275
276 def check_objects(self):
277 """Check that the objects created are still there."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000278 if not self.res.get('objects'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000279 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200280 LOG.info("checking objects")
Sean Dague655e0af2014-05-29 09:00:22 -0400281 for obj in self.res['objects']:
282 client = client_for_user(obj['owner'])
283 r, contents = client.objects.get_object(
284 obj['container'], obj['name'])
285 source = _file_contents(obj['file'])
286 self.assertEqual(contents, source)
287
288 def check_servers(self):
289 """Check that the servers are still up and running."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000290 if not self.res.get('servers'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000291 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200292 LOG.info("checking servers")
Sean Dague655e0af2014-05-29 09:00:22 -0400293 for server in self.res['servers']:
294 client = client_for_user(server['owner'])
295 found = _get_server_by_name(client, server['name'])
296 self.assertIsNotNone(
297 found,
298 "Couldn't find expected server %s" % server['name'])
299
300 r, found = client.servers.get_server(found['id'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200301 # validate neutron is enabled and ironic disabled:
302 if (CONF.service_available.neutron and
303 not CONF.baremetal.driver_enabled):
304 for network_name, body in found['addresses'].items():
305 for addr in body:
306 ip = addr['addr']
307 if addr.get('OS-EXT-IPS:type', 'fixed') == 'fixed':
308 namespace = _get_router_namespace(client,
309 network_name)
310 self._ping_ip(ip, 60, namespace)
311 else:
312 self._ping_ip(ip, 60)
313 else:
314 addr = found['addresses']['private'][0]['addr']
315 self._ping_ip(addr, 60)
316
317 def check_secgroups(self):
318 """Check that the security groups are still existing."""
319 LOG.info("Checking security groups")
320 for secgroup in self.res['secgroups']:
321 client = client_for_user(secgroup['owner'])
322 found = _get_resource_by_name(client.secgroups, 'security_groups',
323 secgroup['name'])
324 self.assertIsNotNone(
325 found,
326 "Couldn't find expected secgroup %s" % secgroup['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400327
Chris Dent878f3782014-06-30 17:04:15 +0100328 def check_telemetry(self):
329 """Check that ceilometer provides a sane sample.
330
331 Confirm that there are more than one sample and that they have the
332 expected metadata.
333
334 If in check mode confirm that the oldest sample available is from
335 before the upgrade.
336 """
Chris Dent0b76f2f2014-10-10 14:24:28 +0100337 if not self.res.get('telemetry'):
338 return
Chris Dent878f3782014-06-30 17:04:15 +0100339 LOG.info("checking telemetry")
340 for server in self.res['servers']:
341 client = client_for_user(server['owner'])
342 response, body = client.telemetry.list_samples(
343 'instance',
344 query=('metadata.display_name', 'eq', server['name'])
345 )
346 self.assertEqual(response.status, 200)
347 self.assertTrue(len(body) >= 1, 'expecting at least one sample')
348 self._confirm_telemetry_sample(server, body[-1])
349
Emilien Macchi626b4f82014-06-15 21:44:29 +0200350 def check_volumes(self):
351 """Check that the volumes are still there and attached."""
Joe Gordon22cd6de2014-07-25 00:16:17 +0000352 if not self.res.get('volumes'):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000353 return
Joe Gordon618c9fb2014-07-16 15:40:01 +0200354 LOG.info("checking volumes")
Emilien Macchi626b4f82014-06-15 21:44:29 +0200355 for volume in self.res['volumes']:
356 client = client_for_user(volume['owner'])
Emilien Macchid18fec12014-09-15 14:32:54 -0400357 vol_body = _get_volume_by_name(client, volume['name'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200358 self.assertIsNotNone(
Emilien Macchid18fec12014-09-15 14:32:54 -0400359 vol_body,
Emilien Macchi626b4f82014-06-15 21:44:29 +0200360 "Couldn't find expected volume %s" % volume['name'])
361
362 # Verify that a volume's attachment retrieved
363 server_id = _get_server_by_name(client, volume['server'])['id']
Emilien Macchid18fec12014-09-15 14:32:54 -0400364 attachment = client.volumes.get_attachment_from_volume(vol_body)
365 self.assertEqual(vol_body['id'], attachment['volume_id'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200366 self.assertEqual(server_id, attachment['server_id'])
367
Chris Dent878f3782014-06-30 17:04:15 +0100368 def _confirm_telemetry_sample(self, server, sample):
369 """Check this sample matches the expected resource metadata."""
370 # Confirm display_name
371 self.assertEqual(server['name'],
372 sample['resource_metadata']['display_name'])
373 # Confirm instance_type of flavor
374 flavor = sample['resource_metadata'].get(
375 'flavor.name',
376 sample['resource_metadata'].get('instance_type')
377 )
378 self.assertEqual(server['flavor'], flavor)
379 # Confirm the oldest sample was created before upgrade.
380 if OPTS.mode == 'check':
381 oldest_timestamp = timeutils.normalize_time(
382 timeutils.parse_isotime(sample['timestamp']))
383 self.assertTrue(
384 oldest_timestamp < JAVELIN_START,
385 'timestamp should come before start of second javelin run'
386 )
387
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200388 def check_networking(self):
389 """Check that the networks are still there."""
390 for res_type in ('networks', 'subnets', 'routers'):
391 for res in self.res[res_type]:
392 client = client_for_user(res['owner'])
393 found = _get_resource_by_name(client.networks, res_type,
394 res['name'])
395 self.assertIsNotNone(
396 found,
397 "Couldn't find expected resource %s" % res['name'])
398
Sean Dague655e0af2014-05-29 09:00:22 -0400399
400#######################
401#
402# OBJECTS
403#
404#######################
405
406
407def _file_contents(fname):
408 with open(fname, 'r') as f:
409 return f.read()
410
411
412def create_objects(objects):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000413 if not objects:
414 return
Sean Dague655e0af2014-05-29 09:00:22 -0400415 LOG.info("Creating objects")
416 for obj in objects:
417 LOG.debug("Object %s" % obj)
418 _assign_swift_role(obj['owner'])
419 client = client_for_user(obj['owner'])
420 client.containers.create_container(obj['container'])
421 client.objects.create_object(
422 obj['container'], obj['name'],
423 _file_contents(obj['file']))
424
Emilien Macchibb71e072014-07-05 19:18:52 +0200425
426def destroy_objects(objects):
427 for obj in objects:
428 client = client_for_user(obj['owner'])
429 r, body = client.objects.delete_object(obj['container'], obj['name'])
Emilien Macchid70f5102014-09-10 09:54:49 -0400430 if not (200 <= int(r['status']) < 299):
Emilien Macchibb71e072014-07-05 19:18:52 +0200431 raise ValueError("unable to destroy object: [%s] %s" % (r, body))
432
433
Sean Dague655e0af2014-05-29 09:00:22 -0400434#######################
435#
436# IMAGES
437#
438#######################
439
440
Sean Dague319b37a2014-07-11 07:28:11 -0400441def _resolve_image(image, imgtype):
442 name = image[imgtype]
443 fname = os.path.join(OPTS.devstack_base, image['imgdir'], name)
444 return name, fname
445
446
Joe Gordon6f0426c2014-07-25 01:10:28 +0000447def _get_image_by_name(client, name):
448 r, body = client.images.image_list()
449 for image in body:
450 if name == image['name']:
451 return image
452 return None
453
454
Sean Dague655e0af2014-05-29 09:00:22 -0400455def create_images(images):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000456 if not images:
457 return
Joe Gordona18d6862014-07-24 22:55:46 +0000458 LOG.info("Creating images")
Sean Dague655e0af2014-05-29 09:00:22 -0400459 for image in images:
460 client = client_for_user(image['owner'])
461
462 # only upload a new image if the name isn't there
Joe Gordon6f0426c2014-07-25 01:10:28 +0000463 if _get_image_by_name(client, image['name']):
Joe Gordona18d6862014-07-24 22:55:46 +0000464 LOG.info("Image '%s' already exists" % image['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400465 continue
466
467 # special handling for 3 part image
468 extras = {}
469 if image['format'] == 'ami':
Sean Dague319b37a2014-07-11 07:28:11 -0400470 name, fname = _resolve_image(image, 'aki')
Sean Dague655e0af2014-05-29 09:00:22 -0400471 r, aki = client.images.create_image(
Sean Dague319b37a2014-07-11 07:28:11 -0400472 'javelin_' + name, 'aki', 'aki')
473 client.images.store_image(aki.get('id'), open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400474 extras['kernel_id'] = aki.get('id')
475
Sean Dague319b37a2014-07-11 07:28:11 -0400476 name, fname = _resolve_image(image, 'ari')
Sean Dague655e0af2014-05-29 09:00:22 -0400477 r, ari = client.images.create_image(
Sean Dague319b37a2014-07-11 07:28:11 -0400478 'javelin_' + name, 'ari', 'ari')
479 client.images.store_image(ari.get('id'), open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400480 extras['ramdisk_id'] = ari.get('id')
481
Sean Dague319b37a2014-07-11 07:28:11 -0400482 _, fname = _resolve_image(image, 'file')
Sean Dague655e0af2014-05-29 09:00:22 -0400483 r, body = client.images.create_image(
484 image['name'], image['format'], image['format'], **extras)
485 image_id = body.get('id')
Sean Dague319b37a2014-07-11 07:28:11 -0400486 client.images.store_image(image_id, open(fname, 'r'))
Sean Dague655e0af2014-05-29 09:00:22 -0400487
488
Joe Gordon6f0426c2014-07-25 01:10:28 +0000489def destroy_images(images):
490 if not images:
491 return
492 LOG.info("Destroying images")
493 for image in images:
494 client = client_for_user(image['owner'])
495
496 response = _get_image_by_name(client, image['name'])
497 if not response:
498 LOG.info("Image '%s' does not exists" % image['name'])
499 continue
500 client.images.delete_image(response['id'])
501
502
Sean Dague655e0af2014-05-29 09:00:22 -0400503#######################
504#
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200505# NETWORKS
506#
507#######################
508
509def _get_router_namespace(client, network):
510 network_id = _get_resource_by_name(client.networks,
511 'networks', network)['id']
David Kranz34e88122014-12-11 15:24:05 -0500512 n_body = client.networks.list_routers()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200513 for router in n_body['routers']:
514 router_id = router['id']
David Kranz34e88122014-12-11 15:24:05 -0500515 r_body = client.networks.list_router_interfaces(router_id)
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200516 for port in r_body['ports']:
517 if port['network_id'] == network_id:
518 return "qrouter-%s" % router_id
519
520
521def _get_resource_by_name(client, resource, name):
522 get_resources = getattr(client, 'list_%s' % resource)
523 if get_resources is None:
524 raise AttributeError("client doesn't have method list_%s" % resource)
David Kranz34e88122014-12-11 15:24:05 -0500525 # Until all tempest client methods are changed to return only one value,
526 # we cannot assume they all have the same signature so we need to discard
527 # the unused response first value it two values are being returned.
528 body = get_resources()
529 if type(body) == tuple:
530 body = body[1]
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200531 if isinstance(body, dict):
532 body = body[resource]
533 for res in body:
534 if name == res['name']:
535 return res
536 raise ValueError('%s not found in %s resources' % (name, resource))
537
538
539def create_networks(networks):
540 LOG.info("Creating networks")
541 for network in networks:
542 client = client_for_user(network['owner'])
543
544 # only create a network if the name isn't here
David Kranz34e88122014-12-11 15:24:05 -0500545 body = client.networks.list_networks()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200546 if any(item['name'] == network['name'] for item in body['networks']):
547 LOG.warning("Dupplicated network name: %s" % network['name'])
548 continue
549
550 client.networks.create_network(name=network['name'])
551
552
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100553def destroy_networks(networks):
554 LOG.info("Destroying subnets")
555 for network in networks:
556 client = client_for_user(network['owner'])
557 network_id = _get_resource_by_name(client.networks, 'networks',
558 network['name'])['id']
559 client.networks.delete_network(network_id)
560
561
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200562def create_subnets(subnets):
563 LOG.info("Creating subnets")
564 for subnet in subnets:
565 client = client_for_user(subnet['owner'])
566
567 network = _get_resource_by_name(client.networks, 'networks',
568 subnet['network'])
569 ip_version = netaddr.IPNetwork(subnet['range']).version
570 # ensure we don't overlap with another subnet in the network
571 try:
572 client.networks.create_subnet(network_id=network['id'],
573 cidr=subnet['range'],
574 name=subnet['name'],
575 ip_version=ip_version)
576 except exceptions.BadRequest as e:
577 is_overlapping_cidr = 'overlaps with another subnet' in str(e)
578 if not is_overlapping_cidr:
579 raise
580
581
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100582def destroy_subnets(subnets):
583 LOG.info("Destroying subnets")
584 for subnet in subnets:
585 client = client_for_user(subnet['owner'])
586 subnet_id = _get_resource_by_name(client.networks,
587 'subnets', subnet['name'])['id']
588 client.networks.delete_subnet(subnet_id)
589
590
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200591def create_routers(routers):
592 LOG.info("Creating routers")
593 for router in routers:
594 client = client_for_user(router['owner'])
595
596 # only create a router if the name isn't here
David Kranz34e88122014-12-11 15:24:05 -0500597 body = client.networks.list_routers()
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200598 if any(item['name'] == router['name'] for item in body['routers']):
599 LOG.warning("Dupplicated router name: %s" % router['name'])
600 continue
601
602 client.networks.create_router(router['name'])
603
604
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100605def destroy_routers(routers):
606 LOG.info("Destroying routers")
607 for router in routers:
608 client = client_for_user(router['owner'])
609 router_id = _get_resource_by_name(client.networks,
610 'routers', router['name'])['id']
611 for subnet in router['subnet']:
612 subnet_id = _get_resource_by_name(client.networks,
613 'subnets', subnet)['id']
614 client.networks.remove_router_interface_with_subnet_id(router_id,
615 subnet_id)
616 client.networks.delete_router(router_id)
617
618
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200619def add_router_interface(routers):
620 for router in routers:
621 client = client_for_user(router['owner'])
622 router_id = _get_resource_by_name(client.networks,
623 'routers', router['name'])['id']
624
625 for subnet in router['subnet']:
626 subnet_id = _get_resource_by_name(client.networks,
627 'subnets', subnet)['id']
628 # connect routers to their subnets
629 client.networks.add_router_interface_with_subnet_id(router_id,
630 subnet_id)
631 # connect routers to exteral network if set to "gateway"
632 if router['gateway']:
633 if CONF.network.public_network_id:
634 ext_net = CONF.network.public_network_id
635 client.networks._update_router(
636 router_id, set_enable_snat=True,
637 external_gateway_info={"network_id": ext_net})
638 else:
639 raise ValueError('public_network_id is not configured.')
640
641
642#######################
643#
Sean Dague655e0af2014-05-29 09:00:22 -0400644# SERVERS
645#
646#######################
647
648def _get_server_by_name(client, name):
649 r, body = client.servers.list_servers()
650 for server in body['servers']:
651 if name == server['name']:
652 return server
653 return None
654
655
Sean Dague655e0af2014-05-29 09:00:22 -0400656def _get_flavor_by_name(client, name):
657 r, body = client.flavors.list_flavors()
658 for flavor in body:
659 if name == flavor['name']:
660 return flavor
661 return None
662
663
664def create_servers(servers):
Joe Gordonb9bcdd82014-07-17 15:44:57 +0000665 if not servers:
666 return
Joe Gordona18d6862014-07-24 22:55:46 +0000667 LOG.info("Creating servers")
Sean Dague655e0af2014-05-29 09:00:22 -0400668 for server in servers:
669 client = client_for_user(server['owner'])
670
671 if _get_server_by_name(client, server['name']):
Joe Gordona18d6862014-07-24 22:55:46 +0000672 LOG.info("Server '%s' already exists" % server['name'])
Sean Dague655e0af2014-05-29 09:00:22 -0400673 continue
674
675 image_id = _get_image_by_name(client, server['image'])['id']
676 flavor_id = _get_flavor_by_name(client, server['flavor'])['id']
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200677 # validate neutron is enabled and ironic disabled
678 kwargs = dict()
679 if (CONF.service_available.neutron and
680 not CONF.baremetal.driver_enabled and server.get('networks')):
681 get_net_id = lambda x: (_get_resource_by_name(
682 client.networks, 'networks', x)['id'])
683 kwargs['networks'] = [{'uuid': get_net_id(network)}
684 for network in server['networks']]
685 resp, body = client.servers.create_server(
686 server['name'], image_id, flavor_id, **kwargs)
Joe Gordon10f260b2014-07-24 23:27:19 +0000687 server_id = body['id']
688 client.servers.wait_for_server_status(server_id, 'ACTIVE')
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200689 # create to security group(s) after server spawning
690 for secgroup in server['secgroups']:
691 client.servers.add_security_group(server_id, secgroup)
Sean Dague655e0af2014-05-29 09:00:22 -0400692
693
Joe Gordondb63b1c2014-07-24 23:21:21 +0000694def destroy_servers(servers):
695 if not servers:
696 return
697 LOG.info("Destroying servers")
698 for server in servers:
699 client = client_for_user(server['owner'])
700
701 response = _get_server_by_name(client, server['name'])
702 if not response:
703 LOG.info("Server '%s' does not exist" % server['name'])
704 continue
705
706 client.servers.delete_server(response['id'])
707 client.servers.wait_for_server_termination(response['id'],
Matthew Treinish1d14c542014-06-17 20:25:40 -0400708 ignore_error=True)
Joe Gordondb63b1c2014-07-24 23:21:21 +0000709
710
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200711def create_secgroups(secgroups):
712 LOG.info("Creating security groups")
713 for secgroup in secgroups:
714 client = client_for_user(secgroup['owner'])
715
716 # only create a security group if the name isn't here
717 # i.e. a security group may be used by another server
718 # only create a router if the name isn't here
719 r, body = client.secgroups.list_security_groups()
720 if any(item['name'] == secgroup['name'] for item in body):
721 LOG.warning("Security group '%s' already exists" %
722 secgroup['name'])
723 continue
724
725 resp, body = client.secgroups.create_security_group(
726 secgroup['name'], secgroup['description'])
727 if not resp_ok(resp):
728 raise ValueError("Failed to create security group: [%s] %s" %
729 (resp, body))
730 secgroup_id = body['id']
731 # for each security group, create the rules
732 for rule in secgroup['rules']:
733 ip_proto, from_port, to_port, cidr = rule.split()
734 client.secgroups.create_security_group_rule(
735 secgroup_id, ip_proto, from_port, to_port, cidr=cidr)
736
737
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100738def destroy_secgroups(secgroups):
739 LOG.info("Destroying security groups")
740 for secgroup in secgroups:
741 client = client_for_user(secgroup['owner'])
742 sg_id = _get_resource_by_name(client.secgroups,
743 'security_groups',
744 secgroup['name'])
745 # sg rules are deleted automatically
746 client.secgroups.delete_security_group(sg_id['id'])
747
748
Sean Dague655e0af2014-05-29 09:00:22 -0400749#######################
750#
Emilien Macchi626b4f82014-06-15 21:44:29 +0200751# VOLUMES
752#
753#######################
754
755def _get_volume_by_name(client, name):
756 r, body = client.volumes.list_volumes()
Emilien Macchid18fec12014-09-15 14:32:54 -0400757 for volume in body:
758 if name == volume['display_name']:
Emilien Macchi626b4f82014-06-15 21:44:29 +0200759 return volume
760 return None
761
762
763def create_volumes(volumes):
Chris Dent51e76de2014-10-01 12:07:14 +0100764 if not volumes:
765 return
766 LOG.info("Creating volumes")
Emilien Macchi626b4f82014-06-15 21:44:29 +0200767 for volume in volumes:
768 client = client_for_user(volume['owner'])
769
770 # only create a volume if the name isn't here
Emilien Macchid18fec12014-09-15 14:32:54 -0400771 if _get_volume_by_name(client, volume['name']):
772 LOG.info("volume '%s' already exists" % volume['name'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200773 continue
774
Emilien Macchid18fec12014-09-15 14:32:54 -0400775 size = volume['gb']
776 v_name = volume['name']
777 resp, body = client.volumes.create_volume(size=size,
778 display_name=v_name)
779 client.volumes.wait_for_volume_status(body['id'], 'available')
Emilien Macchi626b4f82014-06-15 21:44:29 +0200780
781
Emilien Macchibb71e072014-07-05 19:18:52 +0200782def destroy_volumes(volumes):
783 for volume in volumes:
784 client = client_for_user(volume['owner'])
785 volume_id = _get_volume_by_name(client, volume['name'])['id']
Emilien Macchi5ebc27b2014-09-15 14:30:35 -0400786 client.volumes.detach_volume(volume_id)
787 client.volumes.delete_volume(volume_id)
Emilien Macchibb71e072014-07-05 19:18:52 +0200788
789
Emilien Macchi626b4f82014-06-15 21:44:29 +0200790def attach_volumes(volumes):
791 for volume in volumes:
792 client = client_for_user(volume['owner'])
Emilien Macchi626b4f82014-06-15 21:44:29 +0200793 server_id = _get_server_by_name(client, volume['server'])['id']
Emilien Macchid18fec12014-09-15 14:32:54 -0400794 volume_id = _get_volume_by_name(client, volume['name'])['id']
795 device = volume['device']
796 client.volumes.attach_volume(volume_id, server_id, device)
Emilien Macchi626b4f82014-06-15 21:44:29 +0200797
798
799#######################
800#
Sean Dague655e0af2014-05-29 09:00:22 -0400801# MAIN LOGIC
802#
803#######################
804
805def create_resources():
806 LOG.info("Creating Resources")
807 # first create keystone level resources, and we need to be admin
808 # for those.
809 create_tenants(RES['tenants'])
810 create_users(RES['users'])
811 collect_users(RES['users'])
812
813 # next create resources in a well known order
814 create_objects(RES['objects'])
815 create_images(RES['images'])
Emilien Macchi7a2348b2014-06-16 07:32:11 +0200816
817 # validate neutron is enabled and ironic is disabled
818 if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
819 create_networks(RES['networks'])
820 create_subnets(RES['subnets'])
821 create_routers(RES['routers'])
822 add_router_interface(RES['routers'])
823
824 create_secgroups(RES['secgroups'])
Sean Dague655e0af2014-05-29 09:00:22 -0400825 create_servers(RES['servers'])
Emilien Macchid18fec12014-09-15 14:32:54 -0400826 create_volumes(RES['volumes'])
827 attach_volumes(RES['volumes'])
Sean Dague655e0af2014-05-29 09:00:22 -0400828
829
Joe Gordondb63b1c2014-07-24 23:21:21 +0000830def destroy_resources():
831 LOG.info("Destroying Resources")
832 # Destroy in inverse order of create
Joe Gordondb63b1c2014-07-24 23:21:21 +0000833 destroy_servers(RES['servers'])
Joe Gordon6f0426c2014-07-25 01:10:28 +0000834 destroy_images(RES['images'])
Emilien Macchibb71e072014-07-05 19:18:52 +0200835 destroy_objects(RES['objects'])
Emilien Macchibb71e072014-07-05 19:18:52 +0200836 destroy_volumes(RES['volumes'])
Jakub Libosvar3791ac92014-11-11 13:23:44 +0100837 if CONF.service_available.neutron and not CONF.baremetal.driver_enabled:
838 destroy_routers(RES['routers'])
839 destroy_subnets(RES['subnets'])
840 destroy_networks(RES['networks'])
841 destroy_secgroups(RES['secgroups'])
Emilien Macchibb71e072014-07-05 19:18:52 +0200842 destroy_users(RES['users'])
843 destroy_tenants(RES['tenants'])
Joe Gordon6f0426c2014-07-25 01:10:28 +0000844 LOG.warn("Destroy mode incomplete")
845
Joe Gordondb63b1c2014-07-24 23:21:21 +0000846
Sean Dague655e0af2014-05-29 09:00:22 -0400847def get_options():
848 global OPTS
849 parser = argparse.ArgumentParser(
850 description='Create and validate a fixed set of OpenStack resources')
851 parser.add_argument('-m', '--mode',
852 metavar='<create|check|destroy>',
853 required=True,
854 help=('One of (create, check, destroy)'))
855 parser.add_argument('-r', '--resources',
856 required=True,
857 metavar='resourcefile.yaml',
858 help='Resources definition yaml file')
Joe Gordon28a84ae2014-07-17 15:38:28 +0000859
Sean Dague319b37a2014-07-11 07:28:11 -0400860 parser.add_argument(
861 '-d', '--devstack-base',
862 required=True,
863 metavar='/opt/stack/old',
864 help='Devstack base directory for retrieving artifacts')
Joe Gordon28a84ae2014-07-17 15:38:28 +0000865 parser.add_argument(
866 '-c', '--config-file',
867 metavar='/etc/tempest.conf',
868 help='path to javelin2(tempest) config file')
869
Sean Dague655e0af2014-05-29 09:00:22 -0400870 # auth bits, letting us also just source the devstack openrc
871 parser.add_argument('--os-username',
872 metavar='<auth-user-name>',
873 default=os.environ.get('OS_USERNAME'),
874 help=('Defaults to env[OS_USERNAME].'))
875 parser.add_argument('--os-password',
876 metavar='<auth-password>',
877 default=os.environ.get('OS_PASSWORD'),
878 help=('Defaults to env[OS_PASSWORD].'))
879 parser.add_argument('--os-tenant-name',
880 metavar='<auth-tenant-name>',
881 default=os.environ.get('OS_TENANT_NAME'),
882 help=('Defaults to env[OS_TENANT_NAME].'))
883
884 OPTS = parser.parse_args()
885 if OPTS.mode not in ('create', 'check', 'destroy'):
886 print("ERROR: Unknown mode -m %s\n" % OPTS.mode)
887 parser.print_help()
888 sys.exit(1)
Joe Gordon28a84ae2014-07-17 15:38:28 +0000889 if OPTS.config_file:
890 config.CONF.set_config_path(OPTS.config_file)
Sean Dague655e0af2014-05-29 09:00:22 -0400891
892
Joe Gordon915eb8e2014-07-17 11:25:46 +0200893def setup_logging():
Sean Dague655e0af2014-05-29 09:00:22 -0400894 global LOG
Joe Gordon915eb8e2014-07-17 11:25:46 +0200895 logging.setup(__name__)
Sean Dague655e0af2014-05-29 09:00:22 -0400896 LOG = logging.getLogger(__name__)
Sean Dague655e0af2014-05-29 09:00:22 -0400897
898
899def main():
900 global RES
901 get_options()
902 setup_logging()
Chris Dent51e76de2014-10-01 12:07:14 +0100903 RES.update(load_resources(OPTS.resources))
Sean Dague655e0af2014-05-29 09:00:22 -0400904
905 if OPTS.mode == 'create':
906 create_resources()
Joe Gordon1a097002014-07-24 23:44:08 +0000907 # Make sure the resources we just created actually work
908 checker = JavelinCheck(USERS, RES)
909 checker.check()
Sean Dague655e0af2014-05-29 09:00:22 -0400910 elif OPTS.mode == 'check':
911 collect_users(RES['users'])
912 checker = JavelinCheck(USERS, RES)
913 checker.check()
914 elif OPTS.mode == 'destroy':
Joe Gordondb63b1c2014-07-24 23:21:21 +0000915 collect_users(RES['users'])
916 destroy_resources()
Sean Dague655e0af2014-05-29 09:00:22 -0400917 else:
918 LOG.error('Unknown mode %s' % OPTS.mode)
919 return 1
Joe Gordon246353a2014-07-18 00:10:28 +0200920 LOG.info('javelin2 successfully finished')
Sean Dague655e0af2014-05-29 09:00:22 -0400921 return 0
922
923if __name__ == "__main__":
924 sys.exit(main())