Merge "Add key_type selection on Keypairs form"
This commit is contained in:
commit
c27415e822
@ -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"],
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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'])
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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">
|
||||
|
@ -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() {
|
||||
|
@ -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 $}"
|
||||
|
@ -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 = {
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
})();
|
@ -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>
|
@ -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,
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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: ''
|
||||
};
|
||||
|
||||
|
@ -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 $}"
|
||||
|
@ -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 = {
|
||||
|
@ -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'] },
|
||||
|
@ -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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user