Merge "Add create and import key pair actions"
This commit is contained in:
commit
ad17915e43
@ -48,7 +48,7 @@ Default:
|
||||
|
||||
{
|
||||
'images_panel': True,
|
||||
'key_pairs_panel': False,
|
||||
'key_pairs_panel': True,
|
||||
'flavors_panel': False,
|
||||
'domains_panel': False,
|
||||
'users_panel': False,
|
||||
|
@ -1,3 +1,4 @@
|
||||
<action action-classes="'$action-classes$ btn btn-default'">
|
||||
<span class="fa fa-$icon$"></span>
|
||||
$text$
|
||||
</action>
|
||||
|
@ -1,3 +1,4 @@
|
||||
<action action-classes="'$action-classes$'" item="$item$">
|
||||
<span class="fa fa-$icon$"></span>
|
||||
$text$
|
||||
</action>
|
||||
|
@ -208,6 +208,7 @@
|
||||
.replace(
|
||||
'$action-classes$', getActionClasses(action, index, permittedActions.length)
|
||||
)
|
||||
.replace('$icon$', action.template.icon)
|
||||
.replace('$text$', action.template.text)
|
||||
.replace('$title$', action.template.title)
|
||||
.replace('$description$', action.template.description)
|
||||
|
@ -52,7 +52,8 @@
|
||||
maxBytes: '@',
|
||||
key: '@',
|
||||
required: '=',
|
||||
rows: '@'
|
||||
rows: '@',
|
||||
onTextareaChange: '&'
|
||||
},
|
||||
link: link,
|
||||
templateUrl: basePath + 'load-edit.html'
|
||||
@ -113,6 +114,7 @@
|
||||
} else {
|
||||
$scope.textModified = false;
|
||||
}
|
||||
$scope.onTextareaChange({textContent: $scope.textContent});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -327,7 +327,7 @@ SHOW_KEYSTONE_V2_RC = True
|
||||
# Dictionary of currently available angular features
|
||||
ANGULAR_FEATURES = {
|
||||
'images_panel': True,
|
||||
'key_pairs_panel': False,
|
||||
'key_pairs_panel': True,
|
||||
'flavors_panel': False,
|
||||
'domains_panel': False,
|
||||
'users_panel': False,
|
||||
|
@ -21,3 +21,7 @@ hz-details {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textarea#public_key {
|
||||
height: 22em;
|
||||
}
|
@ -30,16 +30,37 @@
|
||||
|
||||
registerKeypairActions.$inject = [
|
||||
'horizon.framework.conf.resource-type-registry.service',
|
||||
'horizon.app.core.keypairs.actions.create.service',
|
||||
'horizon.app.core.keypairs.actions.import.service',
|
||||
'horizon.app.core.keypairs.actions.delete.service',
|
||||
'horizon.app.core.keypairs.resourceType'
|
||||
];
|
||||
|
||||
function registerKeypairActions(
|
||||
registry,
|
||||
createKeypairService,
|
||||
importKeypairService,
|
||||
deleteKeypairService,
|
||||
resourceType
|
||||
) {
|
||||
var keypairResourceType = registry.getResourceType(resourceType);
|
||||
keypairResourceType.globalActions
|
||||
.append({
|
||||
id: 'createKeypairService',
|
||||
service: createKeypairService,
|
||||
template: {
|
||||
type: 'create',
|
||||
text: gettext('Create Key Pair')
|
||||
}
|
||||
})
|
||||
.append({
|
||||
id: 'importKeypairService',
|
||||
service: importKeypairService,
|
||||
template: {
|
||||
text: gettext('Import Public Key'),
|
||||
icon: 'upload'
|
||||
}
|
||||
});
|
||||
keypairResourceType.batchActions
|
||||
.append({
|
||||
id: 'batchDeleteKeypairAction',
|
||||
|
@ -0,0 +1,5 @@
|
||||
<p translate>
|
||||
Key Pairs are how you login to your instance after it is launched.
|
||||
Choose a key pair name you will recognize.
|
||||
Names may only include alphanumeric characters, spaces, or dashes.
|
||||
</p>
|
@ -0,0 +1,159 @@
|
||||
/**
|
||||
* 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 overview
|
||||
* @name horizon.app.core.keypairs.create.service
|
||||
* @description Service for the key pair create modal
|
||||
*/
|
||||
angular
|
||||
.module('horizon.app.core.keypairs.actions')
|
||||
.factory('horizon.app.core.keypairs.actions.create.service', createService);
|
||||
|
||||
createService.$inject = [
|
||||
'horizon.app.core.keypairs.basePath',
|
||||
'horizon.app.core.keypairs.resourceType',
|
||||
'horizon.app.core.openstack-service-api.nova',
|
||||
'horizon.app.core.openstack-service-api.policy',
|
||||
'horizon.framework.util.actions.action-result.service',
|
||||
'horizon.framework.util.file.text-download',
|
||||
'horizon.framework.widgets.form.ModalFormService',
|
||||
'horizon.framework.widgets.toast.service'
|
||||
];
|
||||
|
||||
function createService(
|
||||
basePath, resourceType, nova, policy, actionResult, download, modal, toast
|
||||
) {
|
||||
|
||||
var keypairs = [];
|
||||
var caption = gettext("Create Key Pair");
|
||||
var invalidMsg = gettext("Key pair already exists.");
|
||||
|
||||
// schema
|
||||
var schema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
"name": {
|
||||
title: gettext("Key Pair Name"),
|
||||
type: "string",
|
||||
pattern: "^[A-Za-z0-9 -]+$"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// form
|
||||
var form = [
|
||||
{
|
||||
type: "section",
|
||||
htmlClass: "row",
|
||||
items: [
|
||||
{
|
||||
type: "section",
|
||||
htmlClass: "col-sm-6",
|
||||
items: [
|
||||
{
|
||||
key: "name",
|
||||
validationMessage: {
|
||||
keypairExists: invalidMsg
|
||||
},
|
||||
$validators: {
|
||||
keypairExists: function (name) {
|
||||
return (keypairs.indexOf(name) === -1);
|
||||
}
|
||||
},
|
||||
required: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "section",
|
||||
htmlClass: "col-sm-6",
|
||||
items: [
|
||||
{
|
||||
type: "template",
|
||||
templateUrl: basePath + "actions/create.description.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// model
|
||||
var model;
|
||||
|
||||
var service = {
|
||||
perform: perform,
|
||||
allowed: allowed,
|
||||
getKeypairs: getKeypairs
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
//////////////
|
||||
|
||||
function allowed() {
|
||||
return policy.ifAllowed({ rules: [['compute', 'os_compute_api:os-keypairs:create']] });
|
||||
}
|
||||
|
||||
function perform() {
|
||||
getKeypairs();
|
||||
model = {
|
||||
name: ""
|
||||
};
|
||||
var config = {
|
||||
"title": caption,
|
||||
"submitText": caption,
|
||||
"schema": schema,
|
||||
"form": form,
|
||||
"model": model,
|
||||
"submitIcon": "plus"
|
||||
};
|
||||
return modal.open(config).then(submit);
|
||||
}
|
||||
|
||||
function submit(context) {
|
||||
return nova.createKeypair(context.model).then(success);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name success
|
||||
* @description
|
||||
* Informs the user about the created key pair.
|
||||
* @param {Object} keypair The new key pair object
|
||||
* @returns {undefined} No return value
|
||||
*/
|
||||
function success(response) {
|
||||
var successMsg = gettext('Key pair %(name)s was successfully created.');
|
||||
toast.add('success', interpolate(successMsg, { name: response.data.name }, true));
|
||||
download.downloadTextFile(response.data.private_key, response.data.name + '.pem');
|
||||
var result = actionResult.getActionResult().created(resourceType, response.data.name);
|
||||
return result.result;
|
||||
}
|
||||
|
||||
function getKeypairs() {
|
||||
nova.getKeypairs().then(function(response) {
|
||||
keypairs = response.data.items.map(getName);
|
||||
});
|
||||
}
|
||||
|
||||
function getName(item) {
|
||||
return item.keypair.name;
|
||||
}
|
||||
}
|
||||
})();
|
@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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';
|
||||
|
||||
describe('horizon.app.core.keypairs.actions.create.service', function() {
|
||||
|
||||
var service, nova, $q, $scope, deferred, deferredKeypairs, deferredNewKeypair, toast;
|
||||
var model = {
|
||||
name: "Hiroshige"
|
||||
};
|
||||
var modal = {
|
||||
open: function (config) {
|
||||
config.model = model;
|
||||
deferred = $q.defer();
|
||||
deferred.resolve(config);
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////
|
||||
|
||||
beforeEach(module('horizon.app.core'));
|
||||
beforeEach(module('horizon.framework'));
|
||||
beforeEach(module('horizon.app.core.keypairs.actions'));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('horizon.framework.widgets.form.ModalFormService', modal);
|
||||
}));
|
||||
|
||||
beforeEach(inject(function($injector, _$rootScope_, _$q_) {
|
||||
$scope = _$rootScope_.$new();
|
||||
$q = _$q_;
|
||||
service = $injector.get('horizon.app.core.keypairs.actions.create.service');
|
||||
nova = $injector.get('horizon.app.core.openstack-service-api.nova');
|
||||
toast = $injector.get('horizon.framework.widgets.toast.service');
|
||||
deferredKeypairs = $q.defer();
|
||||
deferredKeypairs.resolve({data: {items: [{keypair: {name: "Hokusai"}}]}});
|
||||
spyOn(nova, 'getKeypairs').and.returnValue(deferredKeypairs.promise);
|
||||
deferredNewKeypair = $q.defer();
|
||||
deferredNewKeypair.resolve({data: {items: [{keypair: {name: "Hiroshige"}}]}});
|
||||
spyOn(nova, 'createKeypair').and.returnValue(deferredNewKeypair.promise);
|
||||
spyOn(modal, 'open').and.callThrough();
|
||||
spyOn(toast, 'add').and.callFake(angular.noop);
|
||||
}));
|
||||
|
||||
it('should check the policy if the user is allowed to create key pair', function() {
|
||||
var allowed = service.allowed();
|
||||
expect(allowed).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should open the modal and submit', inject(function() {
|
||||
service.perform();
|
||||
expect(nova.getKeypairs).toHaveBeenCalled();
|
||||
expect(modal.open).toHaveBeenCalled();
|
||||
|
||||
$scope.$apply();
|
||||
expect(nova.createKeypair).toHaveBeenCalled();
|
||||
expect(toast.add).toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
})();
|
@ -0,0 +1,23 @@
|
||||
<p translate>
|
||||
Key Pairs are how you login to your instance after it is launched.
|
||||
Choose a key pair name you will recognize and paste your SSH public key into the
|
||||
space provided.
|
||||
</p>
|
||||
|
||||
<p translate>
|
||||
There are two ways to generate a key pair. From a Linux system,
|
||||
generate the key pair with the <samp>ssh-keygen</samp> command:
|
||||
</p>
|
||||
<p>
|
||||
<code>ssh-keygen -t rsa -f cloud.key</code>
|
||||
</p>
|
||||
<p translate>
|
||||
This command generates a pair of keys: a private key (cloud.key)
|
||||
and a public key (cloud.key.pub).
|
||||
</p>
|
||||
<p translate>
|
||||
From a Windows system, you can use PuTTYGen to create private/public keys.
|
||||
Use the PuTTY Key Generator to create and save the keys, then copy
|
||||
the public key in the red highlighted box to your <samp>.ssh/authorized_keys</samp>
|
||||
file.
|
||||
</p>
|
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.ImportPublicKeyController
|
||||
* @ngController
|
||||
*
|
||||
* @description
|
||||
* Controller for the loading public key file
|
||||
*/
|
||||
angular
|
||||
.module('horizon.app.core.keypairs.actions')
|
||||
.controller('horizon.app.core.keypairs.actions.ImportPublicKeyController',
|
||||
importPublicKeyController);
|
||||
|
||||
importPublicKeyController.$inject = [
|
||||
'$scope'
|
||||
];
|
||||
|
||||
function importPublicKeyController($scope) {
|
||||
var ctrl = this;
|
||||
ctrl.title = $scope.schema.properties.public_key.title;
|
||||
ctrl.public_key = "";
|
||||
ctrl.onPublicKeyChange = function (publicKey) {
|
||||
$scope.model.public_key = publicKey;
|
||||
};
|
||||
}
|
||||
})();
|
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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';
|
||||
|
||||
describe('horizon.app.core.keypairs.actions.ImportPublicKeyController', function() {
|
||||
var ctrl, scope;
|
||||
|
||||
beforeEach(module('horizon.app.core.keypairs'));
|
||||
beforeEach(inject(function ($injector, _$rootScope_) {
|
||||
scope = _$rootScope_.$new();
|
||||
scope.schema = {
|
||||
properties: {
|
||||
public_key: {
|
||||
title: 'Public Key'
|
||||
}
|
||||
}
|
||||
};
|
||||
scope.model = {
|
||||
public_key: ''
|
||||
};
|
||||
|
||||
var controller = $injector.get('$controller');
|
||||
ctrl = controller(
|
||||
'horizon.app.core.keypairs.actions.ImportPublicKeyController',
|
||||
{ $scope: scope }
|
||||
);
|
||||
}));
|
||||
|
||||
it('gets title from scope.schema.properties.public_key.title', function() {
|
||||
expect(ctrl.title).toBe('Public Key');
|
||||
});
|
||||
|
||||
it('sets public key into scope.model.public_key', function() {
|
||||
var key = 'public key string';
|
||||
ctrl.onPublicKeyChange(key);
|
||||
expect(scope.model.public_key).toBeDefined(key);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})();
|
@ -0,0 +1,9 @@
|
||||
<div ng-controller="horizon.app.core.keypairs.actions.ImportPublicKeyController as ctrl">
|
||||
<load-edit title="{$ ctrl.title $}"
|
||||
model="ctrl.public_key"
|
||||
max-bytes="{$ 16 * 1024 $}"
|
||||
key="public-key"
|
||||
rows=8 required="true"
|
||||
on-textarea-change="ctrl.onPublicKeyChange(textContent)">
|
||||
</load-edit>
|
||||
</div>
|
@ -0,0 +1,157 @@
|
||||
/**
|
||||
* 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 overview
|
||||
* @name horizon.app.core.keypairs.import.service
|
||||
* @description Service for the key pair import modal
|
||||
*/
|
||||
angular
|
||||
.module('horizon.app.core.keypairs.actions')
|
||||
.factory('horizon.app.core.keypairs.actions.import.service', importService);
|
||||
|
||||
importService.$inject = [
|
||||
'horizon.app.core.keypairs.basePath',
|
||||
'horizon.app.core.keypairs.resourceType',
|
||||
'horizon.app.core.openstack-service-api.nova',
|
||||
'horizon.app.core.openstack-service-api.policy',
|
||||
'horizon.framework.util.actions.action-result.service',
|
||||
'horizon.framework.widgets.form.ModalFormService',
|
||||
'horizon.framework.widgets.toast.service'
|
||||
];
|
||||
|
||||
function importService(
|
||||
basePath, resourceType, nova, policy, actionResult, modal, toast
|
||||
) {
|
||||
|
||||
var keypairs = [];
|
||||
var caption = gettext("Import Public Key");
|
||||
var invalidMsg = gettext("Key pair already exists.");
|
||||
|
||||
// schema
|
||||
var schema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
"name": {
|
||||
title: gettext("Key Pair Name"),
|
||||
type: "string",
|
||||
pattern: "^[A-Za-z0-9 -]+$"
|
||||
},
|
||||
"public_key": {
|
||||
title: gettext("Public Key"),
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// form
|
||||
var form = [
|
||||
{
|
||||
type: "section",
|
||||
htmlClass: "row",
|
||||
items: [
|
||||
{
|
||||
type: "section",
|
||||
htmlClass: "col-sm-6",
|
||||
items: [
|
||||
{
|
||||
key: "name",
|
||||
validationMessage: {
|
||||
keypairExists: invalidMsg
|
||||
},
|
||||
$validators: {
|
||||
keypairExists: function (name) {
|
||||
return (keypairs.indexOf(name) === -1);
|
||||
}
|
||||
},
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "template",
|
||||
templateUrl: basePath + "actions/import.public-key.html"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "section",
|
||||
htmlClass: "col-sm-6",
|
||||
items: [
|
||||
{
|
||||
type: "template",
|
||||
templateUrl: basePath + "actions/import.description.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// model
|
||||
var model;
|
||||
|
||||
var service = {
|
||||
perform: perform,
|
||||
allowed: allowed
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
//////////////
|
||||
|
||||
function allowed() {
|
||||
return policy.ifAllowed({ rules: [['compute', 'os_compute_api:os-keypairs:create']] });
|
||||
}
|
||||
|
||||
function perform() {
|
||||
getKeypairs();
|
||||
model = {
|
||||
name: "",
|
||||
public_key: ""
|
||||
};
|
||||
var config = {
|
||||
"title": caption,
|
||||
"submitText": caption,
|
||||
"schema": schema,
|
||||
"form": form,
|
||||
"model": model,
|
||||
"submitIcon": "upload"
|
||||
};
|
||||
return modal.open(config).then(submit);
|
||||
}
|
||||
|
||||
function submit(context) {
|
||||
return nova.createKeypair(context.model).then(success);
|
||||
}
|
||||
|
||||
function success(response) {
|
||||
var successMsg = gettext('Successfully imported key pair %(name)s.');
|
||||
toast.add('success', interpolate(successMsg, { name: response.data.name }, true));
|
||||
var result = actionResult.getActionResult().created(resourceType, response.data.name);
|
||||
return result.result;
|
||||
}
|
||||
|
||||
function getKeypairs() {
|
||||
nova.getKeypairs().then(function(response) {
|
||||
keypairs = response.data.items.map(getName);
|
||||
});
|
||||
}
|
||||
|
||||
function getName(item) {
|
||||
return item.keypair.name;
|
||||
}
|
||||
}
|
||||
})();
|
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* 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';
|
||||
|
||||
describe('horizon.app.core.keypairs.actions.import.service', function() {
|
||||
|
||||
var service, nova, $q, $scope, deferred, deferredKeypairs, deferredNewKeypair, toast;
|
||||
var model = {
|
||||
name: "Hiroshige",
|
||||
public_key: "secret"
|
||||
};
|
||||
var modal = {
|
||||
open: function (config) {
|
||||
config.model = model;
|
||||
deferred = $q.defer();
|
||||
deferred.resolve(config);
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////
|
||||
|
||||
beforeEach(module('horizon.app.core'));
|
||||
beforeEach(module('horizon.framework'));
|
||||
beforeEach(module('horizon.app.core.keypairs.actions'));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('horizon.framework.widgets.form.ModalFormService', modal);
|
||||
}));
|
||||
|
||||
beforeEach(inject(function($injector, _$rootScope_, _$q_) {
|
||||
$scope = _$rootScope_.$new();
|
||||
$q = _$q_;
|
||||
service = $injector.get('horizon.app.core.keypairs.actions.import.service');
|
||||
nova = $injector.get('horizon.app.core.openstack-service-api.nova');
|
||||
toast = $injector.get('horizon.framework.widgets.toast.service');
|
||||
deferredKeypairs = $q.defer();
|
||||
deferredKeypairs.resolve({data: {items: [{keypair: {name: "Hokusai"}}]}});
|
||||
spyOn(nova, 'getKeypairs').and.returnValue(deferredKeypairs.promise);
|
||||
deferredNewKeypair = $q.defer();
|
||||
deferredNewKeypair.resolve({data: {items: [{keypair: {name: "Hiroshige"}}]}});
|
||||
spyOn(nova, 'createKeypair').and.returnValue(deferredNewKeypair.promise);
|
||||
spyOn(modal, 'open').and.callThrough();
|
||||
spyOn(toast, 'add').and.callFake(angular.noop);
|
||||
}));
|
||||
|
||||
it('should check the policy if the user is allowed to import key pair', function() {
|
||||
var allowed = service.allowed();
|
||||
expect(allowed).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should open the modal and submit', inject(function() {
|
||||
service.perform();
|
||||
expect(nova.getKeypairs).toHaveBeenCalled();
|
||||
expect(modal.open).toHaveBeenCalled();
|
||||
|
||||
$scope.$apply();
|
||||
expect(nova.createKeypair).toHaveBeenCalled();
|
||||
expect(toast.add).toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
})();
|
@ -2,7 +2,10 @@
|
||||
features:
|
||||
- |
|
||||
[`blueprint ng-keypairs <https://blueprints.launchpad.net/horizon/+spec/ng-keypairs>`_]
|
||||
Add Angular Key Pairs panel. The Key Pairs panel allows users to view
|
||||
a list of created or imported key pairs. This panel displays a table
|
||||
view of the name and fingerprint of each key pair. Also, public key
|
||||
is shown in expanded row.
|
||||
AngularJS-based Key Pairs panel is added. The features in the legacy
|
||||
panel are fully implemented. The Key Pairs panel now may be configured
|
||||
to use either the legacy or AngularJS-based codes.
|
||||
The ANGULAR_FEATURES setting now allows for a `key_pairs_panel`.
|
||||
If set to True, then the AngularJS-Based Key Pairs panel will be used,
|
||||
while the Django version will be used if set to False. Default value
|
||||
for key_pairs_panel is True.
|
||||
|
Loading…
Reference in New Issue
Block a user