Merge "Add key_type selection on Keypairs form"

This commit is contained in:
Zuul 2019-03-20 10:23:42 +00:00 committed by Gerrit Code Review
commit c27415e822
20 changed files with 194 additions and 31 deletions

View File

@ -35,6 +35,8 @@ MICROVERSION_FEATURES = {
"servergroup_user_info": ["2.13", "2.60"],
"multiattach": ["2.60"],
"auto_allocated_network": ["2.37", "2.42"],
"key_types": ["2.2", "2.9"],
"key_type_list": ["2.9"],
},
"cinder": {
"groups": ["3.27", "3.43", "3.48", "3.58"],

View File

@ -380,13 +380,17 @@ def snapshot_create(request, instance_id, name):
@profiler.trace
def keypair_create(request, name):
return _nova.novaclient(request).keypairs.create(name)
def keypair_create(request, name, key_type='ssh'):
microversion = get_microversion(request, 'key_types')
return _nova.novaclient(request, microversion).\
keypairs.create(name, key_type=key_type)
@profiler.trace
def keypair_import(request, name, public_key):
return _nova.novaclient(request).keypairs.create(name, public_key)
def keypair_import(request, name, public_key, key_type='ssh'):
microversion = get_microversion(request, 'key_types')
return _nova.novaclient(request, microversion).\
keypairs.create(name, public_key, key_type)
@profiler.trace
@ -396,7 +400,8 @@ def keypair_delete(request, name):
@profiler.trace
def keypair_list(request):
return _nova.novaclient(request).keypairs.list()
microversion = get_microversion(request, 'key_type_list')
return _nova.novaclient(request, microversion).keypairs.list()
@profiler.trace

View File

@ -84,9 +84,12 @@ class Keypairs(generic.View):
"""
if 'public_key' in request.DATA:
new = api.nova.keypair_import(request, request.DATA['name'],
request.DATA['public_key'])
request.DATA['public_key'],
request.DATA['key_type'])
else:
new = api.nova.keypair_create(request, request.DATA['name'])
new = api.nova.keypair_create(request,
request.DATA['name'],
request.DATA['key_type'])
return rest_utils.CreatedResponse(
'/api/nova/keypairs/%s' % utils_http.urlquote(new.name),
new.to_dict()

View File

@ -41,16 +41,23 @@ class ImportKeypair(forms.SelfHandlingForm):
label=_("Key Pair Name"),
regex=KEYPAIR_NAME_REGEX,
error_messages=KEYPAIR_ERROR_MESSAGES)
key_type = forms.ChoiceField(label=_("Key Type"),
widget=forms.SelectWidget(),
choices=[('ssh', _("SSH Key")),
('x509', _("X509 Certificate"))],
initial='ssh')
public_key = forms.CharField(label=_("Public Key"),
widget=forms.Textarea())
def handle(self, request, data):
try:
# Remove any new lines in the public key
data['public_key'] = NEW_LINES.sub("", data['public_key'])
# Remove any new lines in the ssh public key
if data['key_type'] == 'ssh':
data['public_key'] = NEW_LINES.sub("", data['public_key'])
keypair = api.nova.keypair_import(request,
data['name'],
data['public_key'])
data['public_key'],
data['key_type'])
messages.success(request,
_('Successfully imported public key: %s')
% data['name'])

View File

@ -118,6 +118,7 @@ class KeyPairsTable(tables.DataTable):
detail_link = "horizon:project:key_pairs:detail"
name = tables.Column("name", verbose_name=_("Key Pair Name"),
link=detail_link)
key_type = tables.Column("type", verbose_name=_("Key Pair Type"))
fingerprint = tables.Column("fingerprint", verbose_name=_("Fingerprint"))
def get_object_id(self, keypair):

View File

@ -104,29 +104,34 @@ class KeyPairTests(test.TestCase):
public_key = "ssh-rsa ABCDEFGHIJKLMNOPQR\r\n" \
"STUVWXYZ1234567890\r" \
"XXYYZZ user@computer\n\n"
key_type = "ssh"
self.mock_keypair_import.return_value = None
formData = {'method': 'ImportKeypair',
'name': key1_name,
'public_key': public_key}
'public_key': public_key,
'key_type': key_type}
url = reverse('horizon:project:key_pairs:import')
res = self.client.post(url, formData)
self.assertMessageCount(res, success=1)
self.mock_keypair_import.assert_called_once_with(
test.IsHttpRequest(), key1_name,
public_key.replace("\r", "").replace("\n", ""))
public_key.replace("\r", "").replace("\n", ""),
key_type)
@test.create_mocks({api.nova: ('keypair_import',)})
def test_import_keypair_invalid_key(self):
key_name = "new_key_pair"
public_key = "ABCDEF"
key_type = "ssh"
self.mock_keypair_import.side_effect = self.exceptions.nova
formData = {'method': 'ImportKeypair',
'name': key_name,
'public_key': public_key}
'public_key': public_key,
'key_type': key_type}
url = reverse('horizon:project:key_pairs:import')
res = self.client.post(url, formData, follow=True)
self.assertEqual(res.redirect_chain, [])
@ -134,15 +139,17 @@ class KeyPairTests(test.TestCase):
self.assertFormErrors(res, count=1, message=msg)
self.mock_keypair_import.assert_called_once_with(
test.IsHttpRequest(), key_name, public_key)
test.IsHttpRequest(), key_name, public_key, key_type)
def test_import_keypair_invalid_key_name(self):
key_name = "invalid#key?name=!"
public_key = "ABCDEF"
key_type = "ssh"
formData = {'method': 'ImportKeypair',
'name': key_name,
'public_key': public_key}
'public_key': public_key,
'key_type': key_type}
url = reverse('horizon:project:key_pairs:import')
res = self.client.post(url, formData, follow=True)
self.assertEqual(res.redirect_chain, [])
@ -152,10 +159,12 @@ class KeyPairTests(test.TestCase):
def test_import_keypair_space_key_name(self):
key_name = " "
public_key = "ABCDEF"
key_type = "ssh"
formData = {'method': 'ImportKeypair',
'name': key_name,
'public_key': public_key}
'public_key': public_key,
'key_type': key_type}
url = reverse('horizon:project:key_pairs:import')
res = self.client.post(url, formData, follow=True)
self.assertEqual(res.redirect_chain, [])
@ -168,15 +177,18 @@ class KeyPairTests(test.TestCase):
public_key = "ssh-rsa ABCDEFGHIJKLMNOPQR\r\n" \
"STUVWXYZ1234567890\r" \
"XXYYZZ user@computer\n\n"
key_type = "ssh"
self.mock_keypair_import.return_value = None
formData = {'method': 'ImportKeypair',
'name': key1_name,
'public_key': public_key}
'public_key': public_key,
'key_type': key_type}
url = reverse('horizon:project:key_pairs:import')
res = self.client.post(url, formData)
self.assertMessageCount(res, success=1)
self.mock_keypair_import.assert_called_once_with(
test.IsHttpRequest(), key1_name,
public_key.replace("\r", "").replace("\n", ""))
public_key.replace("\r", "").replace("\n", ""),
key_type)

View File

@ -46,9 +46,18 @@
ctrl.generate = generate;
ctrl.keypair = '';
ctrl.key_types = {
'ssh': gettext("SSH Key"),
'x509': gettext("X509 Certificate")
};
ctrl.key_type = 'ssh';
ctrl.keypairExistsError = gettext('Keypair already exists or name contains bad characters.');
ctrl.copyPrivateKey = copyPrivateKey;
ctrl.onKeyTypeChange = function (keyType) {
ctrl.key_type = keyType;
};
/*
* @ngdoc function
* @name doesKeypairExist
@ -60,7 +69,7 @@
}
function generate() {
nova.createKeypair({name: ctrl.keypair}).then(onKeypairCreated);
nova.createKeypair({name: ctrl.keypair, key_type: ctrl.key_type}).then(onKeypairCreated);
function onKeypairCreated(data) {
ctrl.createdKeypair = data.data;

View File

@ -28,6 +28,13 @@
ng-show="(ctrl.doesKeypairExist() || wizardForm.$invalid) && wizardForm.$dirty">
{$ ctrl.keypairExistsError $}
</span>
<label class="control-label required" translate>Key Type</label><span class="hz-icon-required fa fa-asterisk"></span>
<select class="form-control switchable ng-pristine ng-untouched ng-valid"
ng-model="key_type"
ng-options="val as label for (val, label) in ctrl.key_types"
name="key-type"
ng-change="ctrl.onKeyTypeChange(key_type)">
</select>
</div>
<div class="form-group" ng-if="ctrl.privateKey">
<label for="private-key">

View File

@ -45,10 +45,17 @@
ctrl.submit = submit;
ctrl.cancel = cancel;
ctrl.model = { name: '', public_key: '' };
ctrl.model = { name: '', public_key: '', key_type: 'ssh' };
ctrl.path = basePath + 'keypair/';
ctrl.title = gettext('Public Key');
ctrl.key_types = {
'ssh': gettext("SSH Key"),
'x509': gettext("X509 Certificate")
};
ctrl.onKeyTypeChange = function (keyType) {
ctrl.model.key_type = keyType;
};
//////////
function submit() {

View File

@ -22,6 +22,13 @@
<input class="form-control" name="name" id="keypair-name"
ng-model="ctrl.model.name"
ng-required="true"/>
<label class="control-label required" translate>Key Type</label>
<span class="hz-icon-required fa fa-asterisk"></span>
<select class="form-control switchable ng-pristine ng-untouched ng-valid"
ng-model="key_type"
ng-options="val as label for (val, label) in ctrl.key_types"
name="key-type"
ng-change="ctrl.onKeyTypeChange(key_type)"></select>
</div>
<load-edit title="{$ ctrl.title $}"

View File

@ -72,7 +72,8 @@
detailsTemplateUrl: basePath + 'keypair/keypair-details.html',
columns: [
{id: 'name', title: gettext('Name'), priority: 1},
{id: 'fingerprint', title: gettext('Fingerprint'), priority: 2}
{id: 'type', title: gettext('Type'), priority: 2},
{id: 'fingerprint', title: gettext('Fingerprint'), priority: 3}
]
};
@ -88,6 +89,10 @@
label: gettext('Fingerprint'),
name: 'fingerprint',
singleton: true
}, {
label: gettext('Type'),
name: 'type',
singleton: true
}];
ctrl.tableLimits = {

View File

@ -0,0 +1,45 @@
/**
* 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.
*/
(function() {
'use strict';
/**
* @ngdoc controller
* @name horizon.app.core.keypairs.actions.CreateKeyPairController
* @ngController
*
* @description
* Controller for the create keypair
*/
angular
.module('horizon.app.core.keypairs.actions')
.controller('horizon.app.core.keypairs.actions.CreateKeypairController',
createKeypairController);
createKeypairController.$inject = [
'$scope'
];
function createKeypairController($scope) {
var ctrl = this;
ctrl.key_types = {
'ssh': gettext("SSH Key"),
'x509': gettext("X509 Certificate")
};
ctrl.onKeyTypeChange = function (keyType) {
$scope.model.key_type = keyType;
};
}
})();

View File

@ -0,0 +1,9 @@
<div ng-controller="horizon.app.core.keypairs.actions.CreateKeypairController as ctrl">
<label class="control-label required" translate>Key Type</label><span class="hz-icon-required fa fa-asterisk"></span>
<select class="form-control switchable ng-pristine ng-untouched ng-valid"
ng-model="key_type"
ng-options="val as label for (val, label) in ctrl.key_types"
name="key-type"
ng-change="ctrl.onKeyTypeChange(key_type)">
</select>
</div>

View File

@ -51,6 +51,10 @@
title: gettext("Key Pair Name"),
type: "string",
pattern: "^[A-Za-z0-9 -_]+$"
},
"key_type": {
title: gettext("Key Type"),
type: "string"
}
}
};
@ -76,6 +80,10 @@
}
},
required: true
},
{
type: "template",
templateUrl: basePath + "actions/create.key-type.html"
}
]
}
@ -102,7 +110,7 @@
function perform() {
getKeypairs();
model = {};
model = { key_type: 'ssh' };
var config = {
"title": caption,
"submitText": caption,

View File

@ -35,7 +35,14 @@
function importPublicKeyController($scope) {
var ctrl = this;
ctrl.title = $scope.schema.properties.public_key.title;
ctrl.key_types = {
'ssh': gettext("SSH Key"),
'x509': gettext("X509 Certificate")
};
ctrl.public_key = "";
ctrl.onKeyTypeChange = function (keyType) {
$scope.model.key_type = keyType;
};
ctrl.onPublicKeyChange = function (publicKey) {
$scope.model.public_key = publicKey;
};

View File

@ -23,12 +23,16 @@
scope = _$rootScope_.$new();
scope.schema = {
properties: {
key_type: {
title: "Key Type"
},
public_key: {
title: 'Public Key'
}
}
};
scope.model = {
key_type: 'ssh',
public_key: ''
};

View File

@ -1,4 +1,7 @@
<div ng-controller="horizon.app.core.keypairs.actions.ImportPublicKeyController as ctrl">
<label class="control-label required" translate>Key Type</label><span class="hz-icon-required fa fa-asterisk"></span>
<select class="form-control switchable ng-pristine ng-untouched ng-valid" ng-model="key_type" ng-options="val as label for (val, label) in ctrl.key_types" name="key-type" ng-change="ctrl.onKeyTypeChange(key_type)">
</select>
<load-edit title="{$ ctrl.title $}"
model="ctrl.public_key"
max-bytes="{$ 16 * 1024 $}"

View File

@ -51,6 +51,10 @@
type: "string",
pattern: "^[A-Za-z0-9 -]+$"
},
"key_type": {
title: gettext("Key Type"),
type: "string"
},
"public_key": {
title: gettext("Public Key"),
type: "string"
@ -109,6 +113,7 @@
function perform() {
getKeypairs();
model = {
key_type: "ssh",
public_key: ""
};
var config = {

View File

@ -60,8 +60,12 @@
urlFunction: keypairsService.urlFunction
})
.append({
id: 'fingerprint',
id: 'type',
priority: 2
})
.append({
id: 'fingerprint',
priority: 3
});
// for magic-search
@ -78,6 +82,7 @@
'id': {},
'keypair_id': {label: gettext('ID'), filters: ['noValue'] },
'name': {label: gettext('Name'), filters: ['noName'] },
'type': {label: gettext('Type'), filters: ['noValue']},
'fingerprint': {label: gettext('Fingerprint'), filters: ['noValue'] },
'created_at': {label: gettext('Created'), filters: ['mediumDate'] },
'user_id': {label: gettext('User ID'), filters: ['noValue'] },

View File

@ -217,33 +217,45 @@ class NovaRestTestCase(test.TestCase):
@test.create_mocks({api.nova: ['keypair_create']})
def test_keypair_create(self):
request = self.mock_rest_request(body='''{"name": "Ni!"}''')
request = self.mock_rest_request(body='''{"name": "Ni!",
"key_type": "ssh"}''')
new = self.mock_keypair_create.return_value
new.to_dict.return_value = {'name': 'Ni!', 'public_key': 'sekrit'}
new.to_dict.return_value = {'name': 'Ni!',
'key_type': 'ssh',
'public_key': 'sekrit'}
new.name = 'Ni!'
with mock.patch.object(settings, 'DEBUG', True):
response = nova.Keypairs().post(request)
self.assertStatusCode(response, 201)
self.assertEqual({"name": "Ni!", "public_key": "sekrit"},
self.assertEqual({"name": "Ni!",
"key_type": "ssh",
"public_key": "sekrit"},
response.json)
self.assertEqual('/api/nova/keypairs/Ni%21', response['location'])
self.mock_keypair_create.assert_called_once_with(request, 'Ni!')
self.mock_keypair_create.assert_called_once_with(request, 'Ni!', 'ssh')
@test.create_mocks({api.nova: ['keypair_import']})
def test_keypair_import(self):
request = self.mock_rest_request(body='''
{"name": "Ni!", "public_key": "hi"}
{"name": "Ni!", "public_key": "hi", "key_type": "ssh"}
''')
new = self.mock_keypair_import.return_value
new.to_dict.return_value = {'name': 'Ni!', 'public_key': 'hi'}
new.to_dict.return_value = {'name': 'Ni!',
'public_key': 'hi',
'key_type': 'ssh'}
new.name = 'Ni!'
with mock.patch.object(settings, 'DEBUG', True):
response = nova.Keypairs().post(request)
self.assertStatusCode(response, 201)
self.assertEqual({"name": "Ni!", "public_key": "hi"},
self.assertEqual({"name": "Ni!",
"public_key": "hi",
"key_type": "ssh"},
response.json)
self.assertEqual('/api/nova/keypairs/Ni%21', response['location'])
self.mock_keypair_import.assert_called_once_with(request, 'Ni!', 'hi')
self.mock_keypair_import.assert_called_once_with(request,
'Ni!',
'hi',
'ssh')
@test.create_mocks({api.nova: ['keypair_get']})
def test_keypair_get(self):