From caa69b4117ebe5f75d45adc805f67035d1d55855 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Sat, 17 Jun 2017 08:46:23 -0500 Subject: [PATCH] Add normalization and functional tests for keypairs Keypairs didn't have functional tests, although they do have ansible tests. Also, there was no normalization. Add both. Change-Id: Ib6fab25cf4c88e5f9d224e831a8b5f297b263aea --- doc/source/model.rst | 20 ++++++++ shade/_normalize.py | 49 +++++++++++++++++++ shade/openstackcloud.py | 10 ++-- shade/tests/fakes.py | 5 +- shade/tests/functional/test_keypairs.py | 62 +++++++++++++++++++++++++ shade/tests/unit/test_keypair.py | 8 ++-- 6 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 shade/tests/functional/test_keypairs.py diff --git a/doc/source/model.rst b/doc/source/model.rst index 70818f586..3293b0306 100644 --- a/doc/source/model.rst +++ b/doc/source/model.rst @@ -130,6 +130,26 @@ A Glance Image. tags=list(), properties=dict()) + +Keypair +------- + +A keypair for a Nova Server. + +.. code-block:: python + + Keypair = dict( + location=Location(), + name=str(), + id=str(), + public_key=str(), + fingerprint=str(), + type=str(), + user_id=str(), + private_key=str() or None + properties=dict()) + + Security Group -------------- diff --git a/shade/_normalize.py b/shade/_normalize.py index b7630f8ed..825a20d25 100644 --- a/shade/_normalize.py +++ b/shade/_normalize.py @@ -12,6 +12,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import datetime import munch import six @@ -44,6 +45,21 @@ _SERVER_FIELDS = ( 'user_id', ) +_KEYPAIR_FIELDS = ( + 'fingerprint', + 'name', + 'private_key', + 'public_key', + 'user_id', +) + +_KEYPAIR_USELESS_FIELDS = ( + 'deleted', + 'deleted_at', + 'id', + 'updated_at', +) + _COMPUTE_LIMITS_FIELDS = ( ('maxPersonality', 'max_personality'), ('maxPersonalitySize', 'max_personality_size'), @@ -203,6 +219,39 @@ class Normalizer(object): return new_flavor + def _normalize_keypairs(self, keypairs): + """Normalize Nova Keypairs""" + ret = [] + for keypair in keypairs: + ret.append(self._normalize_keypair(keypair)) + return ret + + def _normalize_keypair(self, keypair): + """Normalize Ironic Machine""" + + new_keypair = munch.Munch() + keypair = keypair.copy() + + # Discard noise + self._remove_novaclient_artifacts(keypair) + + new_keypair['location'] = self.current_location + for key in _KEYPAIR_FIELDS: + new_keypair[key] = keypair.pop(key, None) + # These are completely meaningless fields + for key in _KEYPAIR_USELESS_FIELDS: + keypair.pop(key, None) + new_keypair['type'] = keypair.pop('type', 'ssh') + # created_at isn't returned from the keypair creation. (what?) + new_keypair['created_at'] = keypair.pop( + 'created_at', datetime.datetime.now().isoformat()) + # Don't even get me started on this + new_keypair['id'] = new_keypair['name'] + + new_keypair['properties'] = keypair.copy() + + return new_keypair + def _normalize_images(self, images): ret = [] for image in images: diff --git a/shade/openstackcloud.py b/shade/openstackcloud.py index 63d8a0c4e..d7563971c 100644 --- a/shade/openstackcloud.py +++ b/shade/openstackcloud.py @@ -1613,7 +1613,8 @@ class OpenStackCloud( """ with _utils.shade_exceptions("Error fetching keypair list"): - return self.manager.submit_task(_tasks.KeypairList()) + return self._normalize_keypairs( + self.manager.submit_task(_tasks.KeypairList())) def list_networks(self, filters=None): """List all available networks. @@ -2959,7 +2960,7 @@ class OpenStackCloud( return _utils._get_entity( _search_one_stack, name_or_id, filters) - def create_keypair(self, name, public_key): + def create_keypair(self, name, public_key=None): """Create a new keypair. :param name: Name of the keypair being created. @@ -2969,8 +2970,9 @@ class OpenStackCloud( """ with _utils.shade_exceptions("Unable to create keypair {name}".format( name=name)): - return self.manager.submit_task(_tasks.KeypairCreate( - name=name, public_key=public_key)) + return self._normalize_keypair( + self.manager.submit_task(_tasks.KeypairCreate( + name=name, public_key=public_key))) def delete_keypair(self, name): """Delete a keypair. diff --git a/shade/tests/fakes.py b/shade/tests/fakes.py index 7967df1dd..d6ab32d50 100644 --- a/shade/tests/fakes.py +++ b/shade/tests/fakes.py @@ -17,6 +17,7 @@ fakes Fakes used for testing """ +import datetime import uuid from shade._heat import template_format @@ -31,6 +32,7 @@ ORCHESTRATION_ENDPOINT = 'https://orchestration.example.com/v1/{p}'.format( p=PROJECT_ID) NO_MD5 = '93b885adfe0da089cdf634904fd59f71' NO_SHA256 = '6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d' +FAKE_PUBLIC_KEY = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCkF3MX59OrlBs3dH5CU7lNmvpbrgZxSpyGjlnE8Flkirnc/Up22lpjznoxqeoTAwTW034k7Dz6aYIrZGmQwe2TkE084yqvlj45Dkyoj95fW/sZacm0cZNuL69EObEGHdprfGJQajrpz22NQoCD8TFB8Wv+8om9NH9Le6s+WPe98WC77KLw8qgfQsbIey+JawPWl4O67ZdL5xrypuRjfIPWjgy/VH85IXg/Z/GONZ2nxHgSShMkwqSFECAC5L3PHB+0+/12M/iikdatFSVGjpuHvkLOs3oe7m6HlOfluSJ85BzLWBbvva93qkGmLg4ZAc8rPh2O+YIsBUHNLLMM/oQp Generated-by-Nova\n" # flake8: noqa def make_fake_flavor(flavor_id, name, ram=100, disk=1600, vcpus=24): @@ -137,7 +139,8 @@ def make_fake_keypair(name): "fingerprint": "7e:eb:ab:24:ba:d1:e1:88:ae:9a:fb:66:53:df:d3:bd", "name": name, "type": "ssh", - "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCkF3MX59OrlBs3dH5CU7lNmvpbrgZxSpyGjlnE8Flkirnc/Up22lpjznoxqeoTAwTW034k7Dz6aYIrZGmQwe2TkE084yqvlj45Dkyoj95fW/sZacm0cZNuL69EObEGHdprfGJQajrpz22NQoCD8TFB8Wv+8om9NH9Le6s+WPe98WC77KLw8qgfQsbIey+JawPWl4O67ZdL5xrypuRjfIPWjgy/VH85IXg/Z/GONZ2nxHgSShMkwqSFECAC5L3PHB+0+/12M/iikdatFSVGjpuHvkLOs3oe7m6HlOfluSJ85BzLWBbvva93qkGmLg4ZAc8rPh2O+YIsBUHNLLMM/oQp Generated-by-Nova\n", # flake8: noqa + "public_key": FAKE_PUBLIC_KEY, + "created_at": datetime.datetime.now().isoformat(), } diff --git a/shade/tests/functional/test_keypairs.py b/shade/tests/functional/test_keypairs.py new file mode 100644 index 000000000..35a159e1c --- /dev/null +++ b/shade/tests/functional/test_keypairs.py @@ -0,0 +1,62 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +test_keypairs +---------------------------------- + +Functional tests for `shade` keypairs methods +""" +from shade.tests import fakes +from shade.tests.functional import base + + +class TestKeypairs(base.BaseFunctionalTestCase): + + def test_create_and_delete(self): + '''Test creating and deleting keypairs functionality''' + name = self.getUniqueString('keypair') + self.addCleanup(self.user_cloud.delete_keypair, name) + keypair = self.user_cloud.create_keypair(name=name) + self.assertEqual(keypair['name'], name) + self.assertIsNotNone(keypair['public_key']) + self.assertIsNotNone(keypair['private_key']) + self.assertIsNotNone(keypair['fingerprint']) + self.assertEqual(keypair['type'], 'ssh') + + keypairs = self.user_cloud.list_keypairs() + self.assertIn(name, [k['name'] for k in keypairs]) + + self.user_cloud.delete_keypair(name) + + keypairs = self.user_cloud.list_keypairs() + self.assertNotIn(name, [k['name'] for k in keypairs]) + + def test_create_and_delete_with_key(self): + '''Test creating and deleting keypairs functionality''' + name = self.getUniqueString('keypair') + self.addCleanup(self.user_cloud.delete_keypair, name) + keypair = self.user_cloud.create_keypair( + name=name, public_key=fakes.FAKE_PUBLIC_KEY) + self.assertEqual(keypair['name'], name) + self.assertIsNotNone(keypair['public_key']) + self.assertIsNone(keypair['private_key']) + self.assertIsNotNone(keypair['fingerprint']) + self.assertEqual(keypair['type'], 'ssh') + + keypairs = self.user_cloud.list_keypairs() + self.assertIn(name, [k['name'] for k in keypairs]) + + self.user_cloud.delete_keypair(name) + + keypairs = self.user_cloud.list_keypairs() + self.assertNotIn(name, [k['name'] for k in keypairs]) diff --git a/shade/tests/unit/test_keypair.py b/shade/tests/unit/test_keypair.py index 8eb87d354..55c3f94f3 100644 --- a/shade/tests/unit/test_keypair.py +++ b/shade/tests/unit/test_keypair.py @@ -43,7 +43,7 @@ class TestKeypair(base.RequestsMockTestCase): new_key = self.cloud.create_keypair( self.keyname, self.key['public_key']) - self.assertEqual(new_key['name'], self.keyname) + self.assertEqual(new_key, self.cloud._normalize_keypair(self.key)) self.assert_calls() @@ -95,10 +95,12 @@ class TestKeypair(base.RequestsMockTestCase): dict(method='GET', uri=self.get_mock_url( 'compute', 'public', append=['os-keypairs']), - json={'keypairs': [self.key]}), + json={'keypairs': [{'keypair': self.key}]}), ]) - self.cloud.list_keypairs() + keypairs = self.cloud.list_keypairs() + self.assertEqual(keypairs, self.cloud._normalize_keypairs([self.key])) + self.assert_calls() def test_list_keypairs_exception(self): self.register_uris([