OpenStack Testing (Tempest) of an existing cloud
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

manager.py 60KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377
  1. # Copyright 2012 OpenStack Foundation
  2. # Copyright 2013 IBM Corp.
  3. # All Rights Reserved.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  6. # not use this file except in compliance with the License. You may obtain
  7. # a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  13. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  14. # License for the specific language governing permissions and limitations
  15. # under the License.
  16. import subprocess
  17. import netaddr
  18. from oslo_log import log
  19. from oslo_serialization import jsonutils as json
  20. from oslo_utils import netutils
  21. from tempest.common import compute
  22. from tempest.common import image as common_image
  23. from tempest.common.utils.linux import remote_client
  24. from tempest.common.utils import net_utils
  25. from tempest.common import waiters
  26. from tempest import config
  27. from tempest import exceptions
  28. from tempest.lib.common.utils import data_utils
  29. from tempest.lib.common.utils import test_utils
  30. from tempest.lib import exceptions as lib_exc
  31. import tempest.test
  32. CONF = config.CONF
  33. LOG = log.getLogger(__name__)
  34. class ScenarioTest(tempest.test.BaseTestCase):
  35. """Base class for scenario tests. Uses tempest own clients. """
  36. credentials = ['primary']
  37. @classmethod
  38. def setup_clients(cls):
  39. super(ScenarioTest, cls).setup_clients()
  40. # Clients (in alphabetical order)
  41. cls.flavors_client = cls.os_primary.flavors_client
  42. cls.compute_floating_ips_client = (
  43. cls.os_primary.compute_floating_ips_client)
  44. if CONF.service_available.glance:
  45. # Check if glance v1 is available to determine which client to use.
  46. if CONF.image_feature_enabled.api_v1:
  47. cls.image_client = cls.os_primary.image_client
  48. elif CONF.image_feature_enabled.api_v2:
  49. cls.image_client = cls.os_primary.image_client_v2
  50. else:
  51. raise lib_exc.InvalidConfiguration(
  52. 'Either api_v1 or api_v2 must be True in '
  53. '[image-feature-enabled].')
  54. # Compute image client
  55. cls.compute_images_client = cls.os_primary.compute_images_client
  56. cls.keypairs_client = cls.os_primary.keypairs_client
  57. # Nova security groups client
  58. cls.compute_security_groups_client = (
  59. cls.os_primary.compute_security_groups_client)
  60. cls.compute_security_group_rules_client = (
  61. cls.os_primary.compute_security_group_rules_client)
  62. cls.servers_client = cls.os_primary.servers_client
  63. cls.interface_client = cls.os_primary.interfaces_client
  64. # Neutron network client
  65. cls.networks_client = cls.os_primary.networks_client
  66. cls.ports_client = cls.os_primary.ports_client
  67. cls.routers_client = cls.os_primary.routers_client
  68. cls.subnets_client = cls.os_primary.subnets_client
  69. cls.floating_ips_client = cls.os_primary.floating_ips_client
  70. cls.security_groups_client = cls.os_primary.security_groups_client
  71. cls.security_group_rules_client = (
  72. cls.os_primary.security_group_rules_client)
  73. # Use the latest available volume clients
  74. if CONF.service_available.cinder:
  75. cls.volumes_client = cls.os_primary.volumes_client_latest
  76. cls.snapshots_client = cls.os_primary.snapshots_client_latest
  77. cls.backups_client = cls.os_primary.backups_client_latest
  78. # ## Test functions library
  79. #
  80. # The create_[resource] functions only return body and discard the
  81. # resp part which is not used in scenario tests
  82. def create_port(self, network_id, client=None, **kwargs):
  83. if not client:
  84. client = self.ports_client
  85. name = data_utils.rand_name(self.__class__.__name__)
  86. if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
  87. kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
  88. if CONF.network.port_profile and 'binding:profile' not in kwargs:
  89. kwargs['binding:profile'] = CONF.network.port_profile
  90. result = client.create_port(
  91. name=name,
  92. network_id=network_id,
  93. **kwargs)
  94. port = result['port']
  95. self.addCleanup(test_utils.call_and_ignore_notfound_exc,
  96. client.delete_port, port['id'])
  97. return port
  98. def create_keypair(self, client=None):
  99. if not client:
  100. client = self.keypairs_client
  101. name = data_utils.rand_name(self.__class__.__name__)
  102. # We don't need to create a keypair by pubkey in scenario
  103. body = client.create_keypair(name=name)
  104. self.addCleanup(client.delete_keypair, name)
  105. return body['keypair']
  106. def create_server(self, name=None, image_id=None, flavor=None,
  107. validatable=False, wait_until='ACTIVE',
  108. clients=None, **kwargs):
  109. """Wrapper utility that returns a test server.
  110. This wrapper utility calls the common create test server and
  111. returns a test server. The purpose of this wrapper is to minimize
  112. the impact on the code of the tests already using this
  113. function.
  114. :param **kwargs:
  115. See extra parameters below
  116. :Keyword Arguments:
  117. * *vnic_type* (``string``) --
  118. used when launching instances with pre-configured ports.
  119. Examples:
  120. normal: a traditional virtual port that is either attached
  121. to a linux bridge or an openvswitch bridge on a
  122. compute node.
  123. direct: an SR-IOV port that is directly attached to a VM
  124. macvtap: an SR-IOV port that is attached to a VM via a macvtap
  125. device.
  126. Defaults to ``CONF.network.port_vnic_type``.
  127. * *port_profile* (``dict``) --
  128. This attribute is a dictionary that can be used (with admin
  129. credentials) to supply information influencing the binding of
  130. the port.
  131. example: port_profile = "capabilities:[switchdev]"
  132. Defaults to ``CONF.network.port_profile``.
  133. """
  134. # NOTE(jlanoux): As a first step, ssh checks in the scenario
  135. # tests need to be run regardless of the run_validation and
  136. # validatable parameters and thus until the ssh validation job
  137. # becomes voting in CI. The test resources management and IP
  138. # association are taken care of in the scenario tests.
  139. # Therefore, the validatable parameter is set to false in all
  140. # those tests. In this way create_server just return a standard
  141. # server and the scenario tests always perform ssh checks.
  142. # Needed for the cross_tenant_traffic test:
  143. if clients is None:
  144. clients = self.os_primary
  145. if name is None:
  146. name = data_utils.rand_name(self.__class__.__name__ + "-server")
  147. vnic_type = kwargs.pop('vnic_type', CONF.network.port_vnic_type)
  148. profile = kwargs.pop('port_profile', CONF.network.port_profile)
  149. # If vnic_type or profile are configured create port for
  150. # every network
  151. if vnic_type or profile:
  152. ports = []
  153. create_port_body = {}
  154. if vnic_type:
  155. create_port_body['binding:vnic_type'] = vnic_type
  156. if profile:
  157. create_port_body['binding:profile'] = profile
  158. if kwargs:
  159. # Convert security group names to security group ids
  160. # to pass to create_port
  161. if 'security_groups' in kwargs:
  162. security_groups = \
  163. clients.security_groups_client.list_security_groups(
  164. ).get('security_groups')
  165. sec_dict = dict([(s['name'], s['id'])
  166. for s in security_groups])
  167. sec_groups_names = [s['name'] for s in kwargs.pop(
  168. 'security_groups')]
  169. security_groups_ids = [sec_dict[s]
  170. for s in sec_groups_names]
  171. if security_groups_ids:
  172. create_port_body[
  173. 'security_groups'] = security_groups_ids
  174. networks = kwargs.pop('networks', [])
  175. else:
  176. networks = []
  177. # If there are no networks passed to us we look up
  178. # for the project's private networks and create a port.
  179. # The same behaviour as we would expect when passing
  180. # the call to the clients with no networks
  181. if not networks:
  182. networks = clients.networks_client.list_networks(
  183. **{'router:external': False, 'fields': 'id'})['networks']
  184. # It's net['uuid'] if networks come from kwargs
  185. # and net['id'] if they come from
  186. # clients.networks_client.list_networks
  187. for net in networks:
  188. net_id = net.get('uuid', net.get('id'))
  189. if 'port' not in net:
  190. port = self.create_port(network_id=net_id,
  191. client=clients.ports_client,
  192. **create_port_body)
  193. ports.append({'port': port['id']})
  194. else:
  195. ports.append({'port': net['port']})
  196. if ports:
  197. kwargs['networks'] = ports
  198. self.ports = ports
  199. tenant_network = self.get_tenant_network()
  200. body, _ = compute.create_test_server(
  201. clients,
  202. tenant_network=tenant_network,
  203. wait_until=wait_until,
  204. name=name, flavor=flavor,
  205. image_id=image_id, **kwargs)
  206. self.addCleanup(waiters.wait_for_server_termination,
  207. clients.servers_client, body['id'])
  208. self.addCleanup(test_utils.call_and_ignore_notfound_exc,
  209. clients.servers_client.delete_server, body['id'])
  210. server = clients.servers_client.show_server(body['id'])['server']
  211. return server
  212. def create_volume(self, size=None, name=None, snapshot_id=None,
  213. imageRef=None, volume_type=None):
  214. if size is None:
  215. size = CONF.volume.volume_size
  216. if imageRef:
  217. if CONF.image_feature_enabled.api_v1:
  218. resp = self.image_client.check_image(imageRef)
  219. image = common_image.get_image_meta_from_headers(resp)
  220. else:
  221. image = self.image_client.show_image(imageRef)
  222. min_disk = image.get('min_disk')
  223. size = max(size, min_disk)
  224. if name is None:
  225. name = data_utils.rand_name(self.__class__.__name__ + "-volume")
  226. kwargs = {'display_name': name,
  227. 'snapshot_id': snapshot_id,
  228. 'imageRef': imageRef,
  229. 'volume_type': volume_type,
  230. 'size': size}
  231. volume = self.volumes_client.create_volume(**kwargs)['volume']
  232. self.addCleanup(self.volumes_client.wait_for_resource_deletion,
  233. volume['id'])
  234. self.addCleanup(test_utils.call_and_ignore_notfound_exc,
  235. self.volumes_client.delete_volume, volume['id'])
  236. self.assertEqual(name, volume['name'])
  237. waiters.wait_for_volume_resource_status(self.volumes_client,
  238. volume['id'], 'available')
  239. # The volume retrieved on creation has a non-up-to-date status.
  240. # Retrieval after it becomes active ensures correct details.
  241. volume = self.volumes_client.show_volume(volume['id'])['volume']
  242. return volume
  243. def create_backup(self, volume_id, name=None, description=None,
  244. force=False, snapshot_id=None, incremental=False,
  245. container=None):
  246. name = name or data_utils.rand_name(
  247. self.__class__.__name__ + "-backup")
  248. kwargs = {'name': name,
  249. 'description': description,
  250. 'force': force,
  251. 'snapshot_id': snapshot_id,
  252. 'incremental': incremental,
  253. 'container': container}
  254. backup = self.backups_client.create_backup(volume_id=volume_id,
  255. **kwargs)['backup']
  256. self.addCleanup(self.backups_client.delete_backup, backup['id'])
  257. waiters.wait_for_volume_resource_status(self.backups_client,
  258. backup['id'], 'available')
  259. return backup
  260. def restore_backup(self, backup_id):
  261. restore = self.backups_client.restore_backup(backup_id)['restore']
  262. self.addCleanup(self.volumes_client.delete_volume,
  263. restore['volume_id'])
  264. waiters.wait_for_volume_resource_status(self.backups_client,
  265. backup_id, 'available')
  266. waiters.wait_for_volume_resource_status(self.volumes_client,
  267. restore['volume_id'],
  268. 'available')
  269. self.assertEqual(backup_id, restore['backup_id'])
  270. return restore
  271. def create_volume_snapshot(self, volume_id, name=None, description=None,
  272. metadata=None, force=False):
  273. name = name or data_utils.rand_name(
  274. self.__class__.__name__ + '-snapshot')
  275. snapshot = self.snapshots_client.create_snapshot(
  276. volume_id=volume_id,
  277. force=force,
  278. display_name=name,
  279. description=description,
  280. metadata=metadata)['snapshot']
  281. self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
  282. snapshot['id'])
  283. self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
  284. waiters.wait_for_volume_resource_status(self.snapshots_client,
  285. snapshot['id'], 'available')
  286. snapshot = self.snapshots_client.show_snapshot(
  287. snapshot['id'])['snapshot']
  288. return snapshot
  289. def _cleanup_volume_type(self, volume_type):
  290. """Clean up a given volume type.
  291. Ensuring all volumes associated to a type are first removed before
  292. attempting to remove the type itself. This includes any image volume
  293. cache volumes stored in a separate tenant to the original volumes
  294. created from the type.
  295. """
  296. admin_volume_type_client = self.os_admin.volume_types_client_latest
  297. admin_volumes_client = self.os_admin.volumes_client_latest
  298. volumes = admin_volumes_client.list_volumes(
  299. detail=True, params={'all_tenants': 1})['volumes']
  300. type_name = volume_type['name']
  301. for volume in [v for v in volumes if v['volume_type'] == type_name]:
  302. test_utils.call_and_ignore_notfound_exc(
  303. admin_volumes_client.delete_volume, volume['id'])
  304. admin_volumes_client.wait_for_resource_deletion(volume['id'])
  305. admin_volume_type_client.delete_volume_type(volume_type['id'])
  306. def create_volume_type(self, client=None, name=None, backend_name=None):
  307. if not client:
  308. client = self.os_admin.volume_types_client_latest
  309. if not name:
  310. class_name = self.__class__.__name__
  311. name = data_utils.rand_name(class_name + '-volume-type')
  312. randomized_name = data_utils.rand_name('scenario-type-' + name)
  313. LOG.debug("Creating a volume type: %s on backend %s",
  314. randomized_name, backend_name)
  315. extra_specs = {}
  316. if backend_name:
  317. extra_specs = {"volume_backend_name": backend_name}
  318. volume_type = client.create_volume_type(
  319. name=randomized_name, extra_specs=extra_specs)['volume_type']
  320. self.addCleanup(self._cleanup_volume_type, volume_type)
  321. return volume_type
  322. def _create_loginable_secgroup_rule(self, secgroup_id=None):
  323. _client = self.compute_security_groups_client
  324. _client_rules = self.compute_security_group_rules_client
  325. if secgroup_id is None:
  326. sgs = _client.list_security_groups()['security_groups']
  327. for sg in sgs:
  328. if sg['name'] == 'default':
  329. secgroup_id = sg['id']
  330. # These rules are intended to permit inbound ssh and icmp
  331. # traffic from all sources, so no group_id is provided.
  332. # Setting a group_id would only permit traffic from ports
  333. # belonging to the same security group.
  334. rulesets = [
  335. {
  336. # ssh
  337. 'ip_protocol': 'tcp',
  338. 'from_port': 22,
  339. 'to_port': 22,
  340. 'cidr': '0.0.0.0/0',
  341. },
  342. {
  343. # ping
  344. 'ip_protocol': 'icmp',
  345. 'from_port': -1,
  346. 'to_port': -1,
  347. 'cidr': '0.0.0.0/0',
  348. }
  349. ]
  350. rules = list()
  351. for ruleset in rulesets:
  352. sg_rule = _client_rules.create_security_group_rule(
  353. parent_group_id=secgroup_id, **ruleset)['security_group_rule']
  354. rules.append(sg_rule)
  355. return rules
  356. def _create_security_group(self):
  357. # Create security group
  358. sg_name = data_utils.rand_name(self.__class__.__name__)
  359. sg_desc = sg_name + " description"
  360. secgroup = self.compute_security_groups_client.create_security_group(
  361. name=sg_name, description=sg_desc)['security_group']
  362. self.assertEqual(secgroup['name'], sg_name)
  363. self.assertEqual(secgroup['description'], sg_desc)
  364. self.addCleanup(
  365. test_utils.call_and_ignore_notfound_exc,
  366. self.compute_security_groups_client.delete_security_group,
  367. secgroup['id'])
  368. # Add rules to the security group
  369. self._create_loginable_secgroup_rule(secgroup['id'])
  370. return secgroup
  371. def get_remote_client(self, ip_address, username=None, private_key=None,
  372. server=None):
  373. """Get a SSH client to a remote server
  374. :param ip_address: the server floating or fixed IP address to use
  375. for ssh validation
  376. :param username: name of the Linux account on the remote server
  377. :param private_key: the SSH private key to use
  378. :param server: server dict, used for debugging purposes
  379. :return: a RemoteClient object
  380. """
  381. if username is None:
  382. username = CONF.validation.image_ssh_user
  383. # Set this with 'keypair' or others to log in with keypair or
  384. # username/password.
  385. if CONF.validation.auth_method == 'keypair':
  386. password = None
  387. if private_key is None:
  388. private_key = self.keypair['private_key']
  389. else:
  390. password = CONF.validation.image_ssh_password
  391. private_key = None
  392. linux_client = remote_client.RemoteClient(
  393. ip_address, username, pkey=private_key, password=password,
  394. server=server, servers_client=self.servers_client)
  395. linux_client.validate_authentication()
  396. return linux_client
  397. def _image_create(self, name, fmt, path,
  398. disk_format=None, properties=None):
  399. if properties is None:
  400. properties = {}
  401. name = data_utils.rand_name('%s-' % name)
  402. params = {
  403. 'name': name,
  404. 'container_format': fmt,
  405. 'disk_format': disk_format or fmt,
  406. }
  407. if CONF.image_feature_enabled.api_v1:
  408. params['is_public'] = 'False'
  409. params['properties'] = properties
  410. params = {'headers': common_image.image_meta_to_headers(**params)}
  411. else:
  412. params['visibility'] = 'private'
  413. # Additional properties are flattened out in the v2 API.
  414. params.update(properties)
  415. body = self.image_client.create_image(**params)
  416. image = body['image'] if 'image' in body else body
  417. self.addCleanup(self.image_client.delete_image, image['id'])
  418. self.assertEqual("queued", image['status'])
  419. with open(path, 'rb') as image_file:
  420. if CONF.image_feature_enabled.api_v1:
  421. self.image_client.update_image(image['id'], data=image_file)
  422. else:
  423. self.image_client.store_image_file(image['id'], image_file)
  424. return image['id']
  425. def glance_image_create(self):
  426. img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
  427. aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
  428. ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
  429. ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
  430. img_container_format = CONF.scenario.img_container_format
  431. img_disk_format = CONF.scenario.img_disk_format
  432. img_properties = CONF.scenario.img_properties
  433. LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
  434. "properties: %s, ami: %s, ari: %s, aki: %s",
  435. img_path, img_container_format, img_disk_format,
  436. img_properties, ami_img_path, ari_img_path, aki_img_path)
  437. try:
  438. image = self._image_create('scenario-img',
  439. img_container_format,
  440. img_path,
  441. disk_format=img_disk_format,
  442. properties=img_properties)
  443. except IOError:
  444. LOG.warning(
  445. "A(n) %s image was not found. Retrying with uec image.",
  446. img_disk_format)
  447. kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
  448. ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
  449. properties = {'kernel_id': kernel, 'ramdisk_id': ramdisk}
  450. image = self._image_create('scenario-ami', 'ami',
  451. path=ami_img_path,
  452. properties=properties)
  453. LOG.debug("image:%s", image)
  454. return image
  455. def _log_console_output(self, servers=None, client=None):
  456. if not CONF.compute_feature_enabled.console_output:
  457. LOG.debug('Console output not supported, cannot log')
  458. return
  459. client = client or self.servers_client
  460. if not servers:
  461. servers = client.list_servers()
  462. servers = servers['servers']
  463. for server in servers:
  464. try:
  465. console_output = client.get_console_output(
  466. server['id'])['output']
  467. LOG.debug('Console output for %s\nbody=\n%s',
  468. server['id'], console_output)
  469. except lib_exc.NotFound:
  470. LOG.debug("Server %s disappeared(deleted) while looking "
  471. "for the console log", server['id'])
  472. def _log_net_info(self, exc):
  473. # network debug is called as part of ssh init
  474. if not isinstance(exc, lib_exc.SSHTimeout):
  475. LOG.debug('Network information on a devstack host')
  476. def create_server_snapshot(self, server, name=None):
  477. # Glance client
  478. _image_client = self.image_client
  479. # Compute client
  480. _images_client = self.compute_images_client
  481. if name is None:
  482. name = data_utils.rand_name(self.__class__.__name__ + 'snapshot')
  483. LOG.debug("Creating a snapshot image for server: %s", server['name'])
  484. image = _images_client.create_image(server['id'], name=name)
  485. image_id = image.response['location'].split('images/')[1]
  486. waiters.wait_for_image_status(_image_client, image_id, 'active')
  487. self.addCleanup(_image_client.wait_for_resource_deletion,
  488. image_id)
  489. self.addCleanup(test_utils.call_and_ignore_notfound_exc,
  490. _image_client.delete_image, image_id)
  491. if CONF.image_feature_enabled.api_v1:
  492. # In glance v1 the additional properties are stored in the headers.
  493. resp = _image_client.check_image(image_id)
  494. snapshot_image = common_image.get_image_meta_from_headers(resp)
  495. image_props = snapshot_image.get('properties', {})
  496. else:
  497. # In glance v2 the additional properties are flattened.
  498. snapshot_image = _image_client.show_image(image_id)
  499. image_props = snapshot_image
  500. bdm = image_props.get('block_device_mapping')
  501. if bdm:
  502. bdm = json.loads(bdm)
  503. if bdm and 'snapshot_id' in bdm[0]:
  504. snapshot_id = bdm[0]['snapshot_id']
  505. self.addCleanup(
  506. self.snapshots_client.wait_for_resource_deletion,
  507. snapshot_id)
  508. self.addCleanup(test_utils.call_and_ignore_notfound_exc,
  509. self.snapshots_client.delete_snapshot,
  510. snapshot_id)
  511. waiters.wait_for_volume_resource_status(self.snapshots_client,
  512. snapshot_id,
  513. 'available')
  514. image_name = snapshot_image['name']
  515. self.assertEqual(name, image_name)
  516. LOG.debug("Created snapshot image %s for server %s",
  517. image_name, server['name'])
  518. return snapshot_image
  519. def nova_volume_attach(self, server, volume_to_attach):
  520. volume = self.servers_client.attach_volume(
  521. server['id'], volumeId=volume_to_attach['id'], device='/dev/%s'
  522. % CONF.compute.volume_device_name)['volumeAttachment']
  523. self.assertEqual(volume_to_attach['id'], volume['id'])
  524. waiters.wait_for_volume_resource_status(self.volumes_client,
  525. volume['id'], 'in-use')
  526. # Return the updated volume after the attachment
  527. return self.volumes_client.show_volume(volume['id'])['volume']
  528. def nova_volume_detach(self, server, volume):
  529. self.servers_client.detach_volume(server['id'], volume['id'])
  530. waiters.wait_for_volume_resource_status(self.volumes_client,
  531. volume['id'], 'available')
  532. def ping_ip_address(self, ip_address, should_succeed=True,
  533. ping_timeout=None, mtu=None, server=None):
  534. timeout = ping_timeout or CONF.validation.ping_timeout
  535. cmd = ['ping', '-c1', '-w1']
  536. if mtu:
  537. cmd += [
  538. # don't fragment
  539. '-M', 'do',
  540. # ping receives just the size of ICMP payload
  541. '-s', str(net_utils.get_ping_payload_size(mtu, 4))
  542. ]
  543. cmd.append(ip_address)
  544. def ping():
  545. proc = subprocess.Popen(cmd,
  546. stdout=subprocess.PIPE,
  547. stderr=subprocess.PIPE)
  548. proc.communicate()
  549. return (proc.returncode == 0) == should_succeed
  550. caller = test_utils.find_test_caller()
  551. LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the'
  552. ' expected result is %(should_succeed)s', {
  553. 'caller': caller, 'ip': ip_address, 'timeout': timeout,
  554. 'should_succeed':
  555. 'reachable' if should_succeed else 'unreachable'
  556. })
  557. result = test_utils.call_until_true(ping, timeout, 1)
  558. LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the '
  559. 'ping result is %(result)s', {
  560. 'caller': caller, 'ip': ip_address, 'timeout': timeout,
  561. 'result': 'expected' if result else 'unexpected'
  562. })
  563. if server:
  564. self._log_console_output([server])
  565. return result
  566. def check_vm_connectivity(self, ip_address,
  567. username=None,
  568. private_key=None,
  569. should_connect=True,
  570. extra_msg="",
  571. server=None,
  572. mtu=None):
  573. """Check server connectivity
  574. :param ip_address: server to test against
  575. :param username: server's ssh username
  576. :param private_key: server's ssh private key to be used
  577. :param should_connect: True/False indicates positive/negative test
  578. positive - attempt ping and ssh
  579. negative - attempt ping and fail if succeed
  580. :param extra_msg: Message to help with debugging if ``ping_ip_address``
  581. fails
  582. :param server: The server whose console to log for debugging
  583. :param mtu: network MTU to use for connectivity validation
  584. :raises: AssertError if the result of the connectivity check does
  585. not match the value of the should_connect param
  586. """
  587. LOG.debug('checking network connections to IP %s with user: %s',
  588. ip_address, username)
  589. if should_connect:
  590. msg = "Timed out waiting for %s to become reachable" % ip_address
  591. else:
  592. msg = "ip address %s is reachable" % ip_address
  593. if extra_msg:
  594. msg = "%s\n%s" % (extra_msg, msg)
  595. self.assertTrue(self.ping_ip_address(ip_address,
  596. should_succeed=should_connect,
  597. mtu=mtu, server=server),
  598. msg=msg)
  599. if should_connect:
  600. # no need to check ssh for negative connectivity
  601. try:
  602. self.get_remote_client(ip_address, username, private_key,
  603. server=server)
  604. except Exception:
  605. if not extra_msg:
  606. extra_msg = 'Failed to ssh to %s' % ip_address
  607. LOG.exception(extra_msg)
  608. raise
  609. def create_floating_ip(self, thing, pool_name=None):
  610. """Create a floating IP and associates to a server on Nova"""
  611. if not pool_name:
  612. pool_name = CONF.network.floating_network_name
  613. floating_ip = (self.compute_floating_ips_client.
  614. create_floating_ip(pool=pool_name)['floating_ip'])
  615. self.addCleanup(test_utils.call_and_ignore_notfound_exc,
  616. self.compute_floating_ips_client.delete_floating_ip,
  617. floating_ip['id'])
  618. self.compute_floating_ips_client.associate_floating_ip_to_server(
  619. floating_ip['ip'], thing['id'])
  620. return floating_ip
  621. def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
  622. private_key=None, server=None):
  623. ssh_client = self.get_remote_client(ip_address,
  624. private_key=private_key,
  625. server=server)
  626. if dev_name is not None:
  627. ssh_client.make_fs(dev_name)
  628. ssh_client.exec_command('sudo mount /dev/%s %s' % (dev_name,
  629. mount_path))
  630. cmd_timestamp = 'sudo sh -c "date > %s/timestamp; sync"' % mount_path
  631. ssh_client.exec_command(cmd_timestamp)
  632. timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
  633. % mount_path)
  634. if dev_name is not None:
  635. ssh_client.exec_command('sudo umount %s' % mount_path)
  636. return timestamp
  637. def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
  638. private_key=None, server=None):
  639. ssh_client = self.get_remote_client(ip_address,
  640. private_key=private_key,
  641. server=server)
  642. if dev_name is not None:
  643. ssh_client.mount(dev_name, mount_path)
  644. timestamp = ssh_client.exec_command('sudo cat %s/timestamp'
  645. % mount_path)
  646. if dev_name is not None:
  647. ssh_client.exec_command('sudo umount %s' % mount_path)
  648. return timestamp
  649. def get_server_ip(self, server):
  650. """Get the server fixed or floating IP.
  651. Based on the configuration we're in, return a correct ip
  652. address for validating that a guest is up.
  653. """
  654. if CONF.validation.connect_method == 'floating':
  655. # The tests calling this method don't have a floating IP
  656. # and can't make use of the validation resources. So the
  657. # method is creating the floating IP there.
  658. return self.create_floating_ip(server)['ip']
  659. elif CONF.validation.connect_method == 'fixed':
  660. # Determine the network name to look for based on config or creds
  661. # provider network resources.
  662. if CONF.validation.network_for_ssh:
  663. addresses = server['addresses'][
  664. CONF.validation.network_for_ssh]
  665. else:
  666. network = self.get_tenant_network()
  667. addresses = (server['addresses'][network['name']]
  668. if network else [])
  669. for address in addresses:
  670. if (address['version'] == CONF.validation.ip_version_for_ssh and # noqa
  671. address['OS-EXT-IPS:type'] == 'fixed'):
  672. return address['addr']
  673. raise exceptions.ServerUnreachable(server_id=server['id'])
  674. else:
  675. raise lib_exc.InvalidConfiguration()
  676. @classmethod
  677. def get_host_for_server(cls, server_id):
  678. server_details = cls.os_admin.servers_client.show_server(server_id)
  679. return server_details['server']['OS-EXT-SRV-ATTR:host']
  680. class NetworkScenarioTest(ScenarioTest):
  681. """Base class for network scenario tests.
  682. This class provide helpers for network scenario tests, using the neutron
  683. API. Helpers from ancestor which use the nova network API are overridden
  684. with the neutron API.
  685. This Class also enforces using Neutron instead of novanetwork.
  686. Subclassed tests will be skipped if Neutron is not enabled
  687. """
  688. credentials = ['primary', 'admin']
  689. @classmethod
  690. def skip_checks(cls):
  691. super(NetworkScenarioTest, cls).skip_checks()
  692. if not CONF.service_available.neutron:
  693. raise cls.skipException('Neutron not available')
  694. def _create_network(self, networks_client=None,
  695. tenant_id=None,
  696. namestart='network-smoke-',
  697. port_security_enabled=True):
  698. if not networks_client:
  699. networks_client = self.networks_client
  700. if not tenant_id:
  701. tenant_id = networks_client.tenant_id
  702. name = data_utils.rand_name(namestart)
  703. network_kwargs = dict(name=name, tenant_id=tenant_id)
  704. # Neutron disables port security by default so we have to check the
  705. # config before trying to create the network with port_security_enabled
  706. if CONF.network_feature_enabled.port_security:
  707. network_kwargs['port_security_enabled'] = port_security_enabled
  708. result = networks_client.create_network(**network_kwargs)
  709. network = result['network']
  710. self.assertEqual(network['name'], name)
  711. self.addCleanup(test_utils.call_and_ignore_notfound_exc,
  712. networks_client.delete_network,
  713. network['id'])
  714. return network
  715. def create_subnet(self, network, subnets_client=None,
  716. namestart='subnet-smoke', **kwargs):
  717. """Create a subnet for the given network
  718. within the cidr block configured for tenant networks.
  719. """
  720. if not subnets_client:
  721. subnets_client = self.subnets_client
  722. def cidr_in_use(cidr, tenant_id):
  723. """Check cidr existence
  724. :returns: True if subnet with cidr already exist in tenant
  725. False else
  726. """
  727. cidr_in_use = self.os_admin.subnets_client.list_subnets(
  728. tenant_id=tenant_id, cidr=cidr)['subnets']
  729. return len(cidr_in_use) != 0
  730. ip_version = kwargs.pop('ip_version', 4)
  731. if ip_version == 6:
  732. tenant_cidr = netaddr.IPNetwork(
  733. CONF.network.project_network_v6_cidr)
  734. num_bits = CONF.network.project_network_v6_mask_bits
  735. else:
  736. tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
  737. num_bits = CONF.network.project_network_mask_bits
  738. result = None
  739. str_cidr = None
  740. # Repeatedly attempt subnet creation with sequential cidr
  741. # blocks until an unallocated block is found.
  742. for subnet_cidr in tenant_cidr.subnet(num_bits):
  743. str_cidr = str(subnet_cidr)
  744. if cidr_in_use(str_cidr, tenant_id=network['tenant_id']):
  745. continue
  746. subnet = dict(
  747. name=data_utils.rand_name(namestart),
  748. network_id=network['id'],
  749. tenant_id=network['tenant_id'],
  750. cidr=str_cidr,
  751. ip_version=ip_version,
  752. **kwargs
  753. )
  754. try:
  755. result = subnets_client.create_subnet(**subnet)
  756. break
  757. except lib_exc.Conflict as e:
  758. is_overlapping_cidr = 'overlaps with another subnet' in str(e)
  759. if not is_overlapping_cidr:
  760. raise
  761. self.assertIsNotNone(result, 'Unable to allocate tenant network')
  762. subnet = result['subnet']
  763. self.assertEqual(subnet['cidr'], str_cidr)
  764. self.addCleanup(test_utils.call_and_ignore_notfound_exc,
  765. subnets_client.delete_subnet, subnet['id'])
  766. return subnet
  767. def _get_server_port_id_and_ip4(self, server, ip_addr=None):
  768. if ip_addr:
  769. ports = self.os_admin.ports_client.list_ports(
  770. device_id=server['id'],
  771. fixed_ips='ip_address=%s' % ip_addr)['ports']
  772. else:
  773. ports = self.os_admin.ports_client.list_ports(
  774. device_id=server['id'])['ports']
  775. # A port can have more than one IP address in some cases.
  776. # If the network is dual-stack (IPv4 + IPv6), this port is associated
  777. # with 2 subnets
  778. p_status = ['ACTIVE']
  779. # NOTE(vsaienko) With Ironic, instances live on separate hardware
  780. # servers. Neutron does not bind ports for Ironic instances, as a
  781. # result the port remains in the DOWN state.
  782. # TODO(vsaienko) remove once bug: #1599836 is resolved.
  783. if getattr(CONF.service_available, 'ironic', False):
  784. p_status.append('DOWN')
  785. port_map = [(p["id"], fxip["ip_address"])
  786. for p in ports
  787. for fxip in p["fixed_ips"]
  788. if (netutils.is_valid_ipv4(fxip["ip_address"]) and
  789. p['status'] in p_status)]
  790. inactive = [p for p in ports if p['status'] != 'ACTIVE']
  791. if inactive:
  792. LOG.warning("Instance has ports that are not ACTIVE: %s", inactive)
  793. self.assertNotEmpty(port_map,
  794. "No IPv4 addresses found in: %s" % ports)
  795. self.assertEqual(len(port_map), 1,
  796. "Found multiple IPv4 addresses: %s. "
  797. "Unable to determine which port to target."
  798. % port_map)
  799. return port_map[0]
  800. def _get_network_by_name(self, network_name):
  801. net = self.os_admin.networks_client.list_networks(
  802. name=network_name)['networks']
  803. self.assertNotEmpty(net,
  804. "Unable to get network by name: %s" % network_name)
  805. return net[0]
  806. def create_floating_ip(self, thing, external_network_id=None,
  807. port_id=None, client=None):
  808. """Create a floating IP and associates to a resource/port on Neutron"""
  809. if not external_network_id:
  810. external_network_id = CONF.network.public_network_id
  811. if not client:
  812. client = self.floating_ips_client
  813. if not port_id:
  814. port_id, ip4 = self._get_server_port_id_and_ip4(thing)
  815. else:
  816. ip4 = None
  817. result = client.create_floatingip(
  818. floating_network_id=external_network_id,
  819. port_id=port_id,
  820. tenant_id=thing['tenant_id'],
  821. fixed_ip_address=ip4
  822. )
  823. floating_ip = result['floatingip']
  824. self.addCleanup(test_utils.call_and_ignore_notfound_exc,
  825. client.delete_floatingip,
  826. floating_ip['id'])
  827. return floating_ip
  828. def check_floating_ip_status(self, floating_ip, status):
  829. """Verifies floatingip reaches the given status
  830. :param dict floating_ip: floating IP dict to check status
  831. :param status: target status
  832. :raises: AssertionError if status doesn't match
  833. """
  834. floatingip_id = floating_ip['id']
  835. def refresh():
  836. result = (self.floating_ips_client.
  837. show_floatingip(floatingip_id)['floatingip'])
  838. return status == result['status']
  839. if not test_utils.call_until_true(refresh,
  840. CONF.network.build_timeout,
  841. CONF.network.build_interval):
  842. floating_ip = self.floating_ips_client.show_floatingip(
  843. floatingip_id)['floatingip']
  844. self.assertEqual(status, floating_ip['status'],
  845. message="FloatingIP: {fp} is at status: {cst}. "
  846. "failed to reach status: {st}"
  847. .format(fp=floating_ip, cst=floating_ip['status'],
  848. st=status))
  849. LOG.info("FloatingIP: {fp} is at status: {st}"
  850. .format(fp=floating_ip, st=status))
  851. def check_tenant_network_connectivity(self, server,
  852. username,
  853. private_key,
  854. should_connect=True,
  855. servers_for_debug=None):
  856. if not CONF.network.project_networks_reachable:
  857. msg = 'Tenant networks not configured to be reachable.'
  858. LOG.info(msg)
  859. return
  860. # The target login is assumed to have been configured for
  861. # key-based authentication by cloud-init.
  862. try:
  863. for ip_addresses in server['addresses'].values():
  864. for ip_address in ip_addresses:
  865. self.check_vm_connectivity(ip_address['addr'],
  866. username,
  867. private_key,
  868. should_connect=should_connect)
  869. except Exception as e:
  870. LOG.exception('Tenant network connectivity check failed')
  871. self._log_console_output(servers_for_debug)
  872. self._log_net_info(e)
  873. raise
  874. def check_remote_connectivity(self, source, dest, should_succeed=True,
  875. nic=None):
  876. """assert ping server via source ssh connection
  877. :param source: RemoteClient: an ssh connection from which to ping
  878. :param dest: an IP to ping against
  879. :param should_succeed: boolean: should ping succeed or not
  880. :param nic: specific network interface to ping from
  881. """
  882. def ping_remote():
  883. try:
  884. source.ping_host(dest, nic=nic)
  885. except lib_exc.SSHExecCommandFailed:
  886. LOG.warning('Failed to ping IP: %s via a ssh connection '
  887. 'from: %s.', dest, source.ssh_client.host)
  888. return not should_succeed
  889. return should_succeed
  890. result = test_utils.call_until_true(ping_remote,
  891. CONF.validation.ping_timeout, 1)
  892. if result:
  893. return
  894. source_host = source.ssh_client.host
  895. if should_succeed:
  896. msg = "Timed out waiting for %s to become reachable from %s" \
  897. % (dest, source_host)
  898. else:
  899. msg = "%s is reachable from %s" % (dest, source_host)
  900. self._log_console_output()
  901. self.fail(msg)
  902. def _create_security_group(self, security_group_rules_client=None,
  903. tenant_id=None,
  904. namestart='secgroup-smoke',
  905. security_groups_client=None):
  906. if security_group_rules_client is None:
  907. security_group_rules_client = self.security_group_rules_client
  908. if security_groups_client is None:
  909. security_groups_client = self.security_groups_client
  910. if tenant_id is None:
  911. tenant_id = security_groups_client.tenant_id
  912. secgroup = self._create_empty_security_group(
  913. namestart=namestart, client=security_groups_client,
  914. tenant_id=tenant_id)
  915. # Add rules to the security group
  916. rules = self._create_loginable_secgroup_rule(
  917. security_group_rules_client=security_group_rules_client,
  918. secgroup=secgroup,
  919. security_groups_client=security_groups_client)
  920. for rule in rules:
  921. self.assertEqual(tenant_id, rule['tenant_id'])
  922. self.assertEqual(secgroup['id'], rule['security_group_id'])
  923. return secgroup
  924. def _create_empty_security_group(self, client=None, tenant_id=None,
  925. namestart='secgroup-smoke'):
  926. """Create a security group without rules.
  927. Default rules will be created:
  928. - IPv4 egress to any
  929. - IPv6 egress to any
  930. :param tenant_id: secgroup will be created in this tenant
  931. :returns: the created security group
  932. """
  933. if client is None:
  934. client = self.security_groups_client
  935. if not tenant_id:
  936. tenant_id = client.tenant_id
  937. sg_name = data_utils.rand_name(namestart)
  938. sg_desc = sg_name + " description"
  939. sg_dict = dict(name=sg_name,
  940. description=sg_desc)
  941. sg_dict['tenant_id'] = tenant_id
  942. result = client.create_security_group(**sg_dict)
  943. secgroup = result['security_group']
  944. self.assertEqual(secgroup['name'], sg_name)
  945. self.assertEqual(tenant_id, secgroup['tenant_id'])
  946. self.assertEqual(secgroup['description'], sg_desc)
  947. self.addCleanup(test_utils.call_and_ignore_notfound_exc,
  948. client.delete_security_group, secgroup['id'])
  949. return secgroup
  950. def _create_security_group_rule(self, secgroup=None,
  951. sec_group_rules_client=None,
  952. tenant_id=None,
  953. security_groups_client=None, **kwargs):
  954. """Create a rule from a dictionary of rule parameters.
  955. Create a rule in a secgroup. if secgroup not defined will search for
  956. default secgroup in tenant_id.
  957. :param secgroup: the security group.
  958. :param tenant_id: if secgroup not passed -- the tenant in which to
  959. search for default secgroup
  960. :param kwargs: a dictionary containing rule parameters:
  961. for example, to allow incoming ssh:
  962. rule = {
  963. direction: 'ingress'
  964. protocol:'tcp',
  965. port_range_min: 22,
  966. port_range_max: 22
  967. }
  968. """
  969. if sec_group_rules_client is None:
  970. sec_group_rules_client = self.security_group_rules_client
  971. if security_groups_client is None:
  972. security_groups_client = self.security_groups_client
  973. if not tenant_id:
  974. tenant_id = security_groups_client.tenant_id
  975. if secgroup is None:
  976. # Get default secgroup for tenant_id
  977. default_secgroups = security_groups_client.list_security_groups(
  978. name='default', tenant_id=tenant_id)['security_groups']
  979. msg = "No default security group for tenant %s." % (tenant_id)
  980. self.assertNotEmpty(default_secgroups, msg)
  981. secgroup = default_secgroups[0]
  982. ruleset = dict(security_group_id=secgroup['id'],
  983. tenant_id=secgroup['tenant_id'])
  984. ruleset.update(kwargs)
  985. sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
  986. sg_rule = sg_rule['security_group_rule']
  987. self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id'])
  988. self.assertEqual(secgroup['id'], sg_rule['security_group_id'])
  989. return sg_rule
  990. def _create_loginable_secgroup_rule(self, security_group_rules_client=None,
  991. secgroup=None,
  992. security_groups_client=None):
  993. """Create loginable security group rule
  994. This function will create:
  995. 1. egress and ingress tcp port 22 allow rule in order to allow ssh
  996. access for ipv4.
  997. 2. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6.
  998. 3. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4.
  999. """
  1000. if security_group_rules_client is None:
  1001. security_group_rules_client = self.security_group_rules_client
  1002. if security_groups_client is None:
  1003. security_groups_client = self.security_groups_client
  1004. rules = []
  1005. rulesets = [
  1006. dict(
  1007. # ssh
  1008. protocol='tcp',
  1009. port_range_min=22,
  1010. port_range_max=22,
  1011. ),
  1012. dict(
  1013. # ping
  1014. protocol='icmp',
  1015. ),
  1016. dict(
  1017. # ipv6-icmp for ping6
  1018. protocol='icmp',
  1019. ethertype='IPv6',
  1020. )
  1021. ]
  1022. sec_group_rules_client = security_group_rules_client
  1023. for ruleset in rulesets:
  1024. for r_direction in ['ingress', 'egress']:
  1025. ruleset['direction'] = r_direction
  1026. try:
  1027. sg_rule = self._create_security_group_rule(
  1028. sec_group_rules_client=sec_group_rules_client,
  1029. secgroup=secgroup,
  1030. security_groups_client=security_groups_client,
  1031. **ruleset)
  1032. except lib_exc.Conflict as ex:
  1033. # if rule already exist - skip rule and continue
  1034. msg = 'Security group rule already exists'
  1035. if msg not in ex._error_string:
  1036. raise ex
  1037. else:
  1038. self.assertEqual(r_direction, sg_rule['direction'])
  1039. rules.append(sg_rule)
  1040. return rules
  1041. def _get_router(self, client=None, tenant_id=None):
  1042. """Retrieve a router for the given tenant id.
  1043. If a public router has been configured, it will be returned.
  1044. If a public router has not been configured, but a public
  1045. network has, a tenant router will be created and returned that
  1046. routes traffic to the public network.
  1047. """
  1048. if not client:
  1049. client = self.routers_client
  1050. if not tenant_id:
  1051. tenant_id = client.tenant_id
  1052. router_id = CONF.network.public_router_id
  1053. network_id = CONF.network.public_network_id
  1054. if router_id:
  1055. body = client.show_router(router_id)
  1056. return body['router']
  1057. elif network_id:
  1058. router = client.create_router(
  1059. name=data_utils.rand_name(self.__class__.__name__ + '-router'),
  1060. admin_state_up=True,
  1061. tenant_id=tenant_id,
  1062. external_gateway_info=dict(network_id=network_id))['router']
  1063. self.addCleanup(test_utils.call_and_ignore_notfound_exc,
  1064. client.delete_router, router['id'])
  1065. return router
  1066. else:
  1067. raise Exception("Neither of 'public_router_id' or "
  1068. "'public_network_id' has been defined.")
  1069. def create_networks(self, networks_client=None,
  1070. routers_client=None, subnets_client=None,
  1071. tenant_id=None, dns_nameservers=None,
  1072. port_security_enabled=True):
  1073. """Create a network with a subnet connected to a router.
  1074. The baremetal driver is a special case since all nodes are
  1075. on the same shared network.
  1076. :param tenant_id: id of tenant to create resources in.
  1077. :param dns_nameservers: list of dns servers to send to subnet.
  1078. :returns: network, subnet, router
  1079. """
  1080. if CONF.network.shared_physical_network:
  1081. # NOTE(Shrews): This exception is for environments where tenant
  1082. # credential isolation is available, but network separation is
  1083. # not (the current baremetal case). Likely can be removed when
  1084. # test account mgmt is reworked:
  1085. # https://blueprints.launchpad.net/tempest/+spec/test-accounts
  1086. if not CONF.compute.fixed_network_name:
  1087. m = 'fixed_network_name must be specified in config'
  1088. raise lib_exc.InvalidConfiguration(m)
  1089. network = self._get_network_by_name(
  1090. CONF.compute.fixed_network_name)
  1091. router = None
  1092. subnet = None
  1093. else:
  1094. network = self._create_network(
  1095. networks_client=networks_client,
  1096. tenant_id=tenant_id,
  1097. port_security_enabled=port_security_enabled)
  1098. router = self._get_router(client=routers_client,
  1099. tenant_id=tenant_id)
  1100. subnet_kwargs = dict(network=network,
  1101. subnets_client=subnets_client)
  1102. # use explicit check because empty list is a valid option
  1103. if dns_nameservers is not None:
  1104. subnet_kwargs['dns_nameservers'] = dns_nameservers
  1105. subnet = self.create_subnet(**subnet_kwargs)
  1106. if not routers_client:
  1107. routers_client = self.routers_client
  1108. router_id = router['id']
  1109. routers_client.add_router_interface(router_id,
  1110. subnet_id=subnet['id'])
  1111. # save a cleanup job to remove this association between
  1112. # router and subnet
  1113. self.addCleanup(test_utils.call_and_ignore_notfound_exc,
  1114. routers_client.remove_router_interface, router_id,
  1115. subnet_id=subnet['id'])
  1116. return network, subnet, router
  1117. class EncryptionScenarioTest(ScenarioTest):
  1118. """Base class for encryption scenario tests"""
  1119. credentials = ['primary', 'admin']
  1120. @classmethod
  1121. def setup_clients(cls):
  1122. super(EncryptionScenarioTest, cls).setup_clients()
  1123. cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
  1124. cls.admin_encryption_types_client =\
  1125. cls.os_admin.encryption_types_client_latest
  1126. def create_encryption_type(self, client=None, type_id=None, provider=None,
  1127. key_size=None, cipher=None,
  1128. control_location=None):
  1129. if not client:
  1130. client = self.admin_encryption_types_client
  1131. if not type_id:
  1132. volume_type = self.create_volume_type()
  1133. type_id = volume_type['id']
  1134. LOG.debug("Creating an encryption type for volume type: %s", type_id)
  1135. client.create_encryption_type(
  1136. type_id, provider=provider, key_size=key_size, cipher=cipher,
  1137. control_location=control_location)
  1138. def create_encrypted_volume(self, encryption_provider, volume_type,
  1139. key_size=256, cipher='aes-xts-plain64',
  1140. control_location='front-end'):
  1141. volume_type = self.create_volume_type(name=volume_type)
  1142. self.create_encryption_type(type_id=volume_type['id'],
  1143. provider=encryption_provider,
  1144. key_size=key_size,
  1145. cipher=cipher,
  1146. control_location=control_location)
  1147. return self.create_volume(volume_type=volume_type['name'])
  1148. class ObjectStorageScenarioTest(ScenarioTest):
  1149. """Provide harness to do Object Storage scenario tests.
  1150. Subclasses implement the tests that use the methods provided by this
  1151. class.
  1152. """
  1153. @classmethod
  1154. def skip_checks(cls):
  1155. super(ObjectStorageScenarioTest, cls).skip_checks()
  1156. if not CONF.service_available.swift:
  1157. skip_msg = ("%s skipped as swift is not available" %
  1158. cls.__name__)
  1159. raise cls.skipException(skip_msg)
  1160. @classmethod
  1161. def setup_credentials(cls):
  1162. cls.set_network_resources()
  1163. super(ObjectStorageScenarioTest, cls).setup_credentials()
  1164. operator_role = CONF.object_storage.operator_role
  1165. cls.os_operator = cls.get_client_manager(roles=[operator_role])
  1166. @classmethod
  1167. def setup_clients(cls):
  1168. super(ObjectStorageScenarioTest, cls).setup_clients()
  1169. # Clients for Swift
  1170. cls.account_client = cls.os_operator.account_client
  1171. cls.container_client = cls.os_operator.container_client
  1172. cls.object_client = cls.os_operator.object_client
  1173. def get_swift_stat(self):
  1174. """get swift status for our user account."""
  1175. self.account_client.list_account_containers()
  1176. LOG.debug('Swift status information obtained successfully')
  1177. def create_container(self, container_name=None):
  1178. name = container_name or data_utils.rand_name(
  1179. 'swift-scenario-container')
  1180. self.container_client.update_container(name)
  1181. # look for the container to assure it is created
  1182. self.list_and_check_container_objects(name)
  1183. LOG.debug('Container %s created', name)
  1184. self.addCleanup(test_utils.call_and_ignore_notfound_exc,
  1185. self.container_client.delete_container,
  1186. name)
  1187. return name
  1188. def delete_container(self, container_name):
  1189. self.container_client.delete_container(container_name)
  1190. LOG.debug('Container %s deleted', container_name)
  1191. def upload_object_to_container(self, container_name, obj_name=None):
  1192. obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
  1193. obj_data = data_utils.random_bytes()
  1194. self.object_client.create_object(container_name, obj_name, obj_data)
  1195. self.addCleanup(test_utils.call_and_ignore_notfound_exc,
  1196. self.object_client.delete_object,
  1197. container_name,
  1198. obj_name)
  1199. return obj_name, obj_data
  1200. def delete_object(self, container_name, filename):
  1201. self.object_client.delete_object(container_name, filename)
  1202. self.list_and_check_container_objects(container_name,
  1203. not_present_obj=[filename])
  1204. def list_and_check_container_objects(self, container_name,
  1205. present_obj=None,
  1206. not_present_obj=None):
  1207. # List objects for a given container and assert which are present and
  1208. # which are not.
  1209. if present_obj is None:
  1210. present_obj = []
  1211. if not_present_obj is None:
  1212. not_present_obj = []
  1213. _, object_list = self.container_client.list_container_objects(
  1214. container_name)
  1215. if present_obj:
  1216. for obj in present_obj:
  1217. self.assertIn(obj, object_list)
  1218. if not_present_obj:
  1219. for obj in not_present_obj:
  1220. self.assertNotIn(obj, object_list)
  1221. def download_and_verify(self, container_name, obj_name, expected_data):
  1222. _, obj = self.object_client.get_object(container_name, obj_name)
  1223. self.assertEqual(obj, expected_data)