Merge "Add normalization and functional tests for keypairs"
This commit is contained in:
commit
8bd5f485cc
@ -130,6 +130,26 @@ A Glance Image.
|
|||||||
tags=list(),
|
tags=list(),
|
||||||
properties=dict())
|
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
|
Security Group
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
import datetime
|
||||||
import munch
|
import munch
|
||||||
import six
|
import six
|
||||||
|
|
||||||
@ -44,6 +45,21 @@ _SERVER_FIELDS = (
|
|||||||
'user_id',
|
'user_id',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_KEYPAIR_FIELDS = (
|
||||||
|
'fingerprint',
|
||||||
|
'name',
|
||||||
|
'private_key',
|
||||||
|
'public_key',
|
||||||
|
'user_id',
|
||||||
|
)
|
||||||
|
|
||||||
|
_KEYPAIR_USELESS_FIELDS = (
|
||||||
|
'deleted',
|
||||||
|
'deleted_at',
|
||||||
|
'id',
|
||||||
|
'updated_at',
|
||||||
|
)
|
||||||
|
|
||||||
_COMPUTE_LIMITS_FIELDS = (
|
_COMPUTE_LIMITS_FIELDS = (
|
||||||
('maxPersonality', 'max_personality'),
|
('maxPersonality', 'max_personality'),
|
||||||
('maxPersonalitySize', 'max_personality_size'),
|
('maxPersonalitySize', 'max_personality_size'),
|
||||||
@ -203,6 +219,39 @@ class Normalizer(object):
|
|||||||
|
|
||||||
return new_flavor
|
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):
|
def _normalize_images(self, images):
|
||||||
ret = []
|
ret = []
|
||||||
for image in images:
|
for image in images:
|
||||||
|
@ -1610,7 +1610,8 @@ class OpenStackCloud(
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
with _utils.shade_exceptions("Error fetching keypair list"):
|
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):
|
def list_networks(self, filters=None):
|
||||||
"""List all available networks.
|
"""List all available networks.
|
||||||
@ -2956,7 +2957,7 @@ class OpenStackCloud(
|
|||||||
return _utils._get_entity(
|
return _utils._get_entity(
|
||||||
_search_one_stack, name_or_id, filters)
|
_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.
|
"""Create a new keypair.
|
||||||
|
|
||||||
:param name: Name of the keypair being created.
|
:param name: Name of the keypair being created.
|
||||||
@ -2966,8 +2967,9 @@ class OpenStackCloud(
|
|||||||
"""
|
"""
|
||||||
with _utils.shade_exceptions("Unable to create keypair {name}".format(
|
with _utils.shade_exceptions("Unable to create keypair {name}".format(
|
||||||
name=name)):
|
name=name)):
|
||||||
return self.manager.submit_task(_tasks.KeypairCreate(
|
return self._normalize_keypair(
|
||||||
name=name, public_key=public_key))
|
self.manager.submit_task(_tasks.KeypairCreate(
|
||||||
|
name=name, public_key=public_key)))
|
||||||
|
|
||||||
def delete_keypair(self, name):
|
def delete_keypair(self, name):
|
||||||
"""Delete a keypair.
|
"""Delete a keypair.
|
||||||
|
@ -17,6 +17,7 @@ fakes
|
|||||||
Fakes used for testing
|
Fakes used for testing
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from shade._heat import template_format
|
from shade._heat import template_format
|
||||||
@ -31,6 +32,7 @@ ORCHESTRATION_ENDPOINT = 'https://orchestration.example.com/v1/{p}'.format(
|
|||||||
p=PROJECT_ID)
|
p=PROJECT_ID)
|
||||||
NO_MD5 = '93b885adfe0da089cdf634904fd59f71'
|
NO_MD5 = '93b885adfe0da089cdf634904fd59f71'
|
||||||
NO_SHA256 = '6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d'
|
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):
|
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",
|
"fingerprint": "7e:eb:ab:24:ba:d1:e1:88:ae:9a:fb:66:53:df:d3:bd",
|
||||||
"name": name,
|
"name": name,
|
||||||
"type": "ssh",
|
"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(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
62
shade/tests/functional/test_keypairs.py
Normal file
62
shade/tests/functional/test_keypairs.py
Normal file
@ -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])
|
@ -43,7 +43,7 @@ class TestKeypair(base.RequestsMockTestCase):
|
|||||||
|
|
||||||
new_key = self.cloud.create_keypair(
|
new_key = self.cloud.create_keypair(
|
||||||
self.keyname, self.key['public_key'])
|
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()
|
self.assert_calls()
|
||||||
|
|
||||||
@ -95,10 +95,12 @@ class TestKeypair(base.RequestsMockTestCase):
|
|||||||
dict(method='GET',
|
dict(method='GET',
|
||||||
uri=self.get_mock_url(
|
uri=self.get_mock_url(
|
||||||
'compute', 'public', append=['os-keypairs']),
|
'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):
|
def test_list_keypairs_exception(self):
|
||||||
self.register_uris([
|
self.register_uris([
|
||||||
|
Loading…
x
Reference in New Issue
Block a user