Launch Instance Step - Key Pair Import
This adds key pair creation and importing to Launch Instance. Closes-Bug: #1433826 Change-Id: I548bcea280cea1ebcb7445c519bfdbfe811a9235 Co-Authored-By: Neill Cox <neill.cox@ingenious.com.au> Co-Authored-By: Richard Jones <r1chardj0n3s@gmail.com> Co-Authored-By: Kelly Domico <kelly.domico@hp.com> Co-Authored-By: Mike Hagedorn <mike.hagedorn@hp.com> Co-Authored-By: Brian Tully <brian.tully@hp.com> Co-Authored-By: Shaoquan Chen <sean.chen2@hp.com>
This commit is contained in:
parent
c6b2bc7e22
commit
86d121c8f2
@ -59,7 +59,7 @@ $em-per-priority: floor($table-col-avg-width / $font-size-base) * 3;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
@include search-placeholder {
|
||||
@include input-placeholder {
|
||||
font-weight: normal;
|
||||
color: $placeholder-text-color;
|
||||
}
|
||||
|
@ -297,3 +297,10 @@
|
||||
margin-top: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-control {
|
||||
@include input-placeholder {
|
||||
font-weight: normal;
|
||||
color: $placeholder-text-color;
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,11 @@ limitations under the License.
|
||||
this.createKeypair = function(newKeypair) {
|
||||
return apiService.post('/api/nova/keypairs/', newKeypair)
|
||||
.error(function () {
|
||||
horizon.alert('error', gettext('Unable to create the keypair.'));
|
||||
if (angular.isDefined(newKeypair.public_key)) {
|
||||
horizon.alert('error', gettext('Unable to import the keypair.'));
|
||||
} else {
|
||||
horizon.alert('error', gettext('Unable to create the keypair.'));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,34 @@
|
||||
<div class="ng-wizard no-navigation" ng-form="wizardForm">
|
||||
<div class="title" ng-bind="::ctrl.labels.wizardTitle"></div>
|
||||
|
||||
<div class="step">
|
||||
<h1>{$ ::ctrl.labels.title $}</h1>
|
||||
<div class="content">
|
||||
<div class="subtitle">{$ ::ctrl.labels.help $}</div>
|
||||
<div class="form-group">
|
||||
<div class="form-field required">
|
||||
<label>{$ ::ctrl.labels.keyPairName $}</label>
|
||||
<input class="form-control" name="name"
|
||||
ng-model="ctrl.model.name"
|
||||
ng-required="true" placeholder="{$ ::ctrl.labels.required $}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toolbar">
|
||||
<div class="secondary-btn-grp">
|
||||
<button class="cancel btn btn-sm btn-default" ng-click="ctrl.cancel()">
|
||||
<span class="fa fa-close"></span>
|
||||
{$ ::ctrl.labels.cancel $}
|
||||
</button>
|
||||
</div>
|
||||
<div class="primary-btn-grp">
|
||||
<button class="finish btn btn-sm btn-success"
|
||||
ng-click="ctrl.submit()" ng-disabled="wizardForm.$invalid">
|
||||
<span class="fa fa-plus"></span>
|
||||
{$ ::ctrl.labels.ok $}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,50 @@
|
||||
<div class="ng-wizard no-navigation" ng-form="wizardForm">
|
||||
<div class="title" ng-bind="::ctrl.labels.wizardTitle"></div>
|
||||
|
||||
<div class="step">
|
||||
<h1>{$ ::ctrl.labels.title $}</h1>
|
||||
<div class="content">
|
||||
<div class="subtitle">{$ ::ctrl.labels.help $}</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-field required name">
|
||||
<label>{$ ::ctrl.labels.keyPairName $}</label>
|
||||
<input class="form-control" name="name"
|
||||
ng-model="ctrl.model.name"
|
||||
ng-required="true" placeholder="{$ ::ctrl.labels.required $}"/>
|
||||
</div>
|
||||
<div class="form-field required key">
|
||||
<label>{$ ::ctrl.labels.publicKey $}</label>
|
||||
<textarea class="form-control" name="key" rows="15"
|
||||
ng-model="ctrl.model.public_key"
|
||||
ng-required="true" placeholder="{$ ::ctrl.labels.required $}">
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toolbar">
|
||||
<div class="secondary-btn-grp">
|
||||
<button class="cancel btn btn-sm btn-default" ng-click="ctrl.cancel()">
|
||||
<span class="fa fa-close"></span>
|
||||
{$ ::ctrl.labels.cancel $}
|
||||
</button>
|
||||
</div>
|
||||
<div class="primary-btn-grp">
|
||||
<button class="finish btn btn-sm btn-success"
|
||||
ng-click="ctrl.submit()" ng-disabled="wizardForm.$invalid">
|
||||
<span class="fa fa-upload"></span>
|
||||
{$ ::ctrl.labels.ok $}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<help-panel>
|
||||
<div ng-controller="LaunchInstanceImportKeyPairHelpCtrl as importHelpCtrl">
|
||||
<h1>{$ ::importHelpCtrl.title $}</h1>
|
||||
<p ng-repeat="paragraph in ::importHelpCtrl.paragraphs"
|
||||
ng-bind-html="::paragraph"></p>
|
||||
</div>
|
||||
</help-panel>
|
||||
</div>
|
@ -1,6 +1,6 @@
|
||||
<dl class="dl-horizontal key-pair-details">
|
||||
<dt>{$ ctrl.tableLabels.public_key $}</dt>
|
||||
<dd>
|
||||
<pre><code>{$ row.public_key $}</code></pre>
|
||||
<pre><code>{$ row.public_key $}</code></pre>
|
||||
</dd>
|
||||
</dl>
|
||||
|
@ -4,68 +4,82 @@
|
||||
<div class="content">
|
||||
<div class="subtitle">{$ ::ctrl.label.subtitle $}</div>
|
||||
|
||||
<transfer-table tr-model="ctrl.tableData"
|
||||
help-text="ctrl.tableHelp"
|
||||
limits="ctrl.tableLimits">
|
||||
<div class="row form-group">
|
||||
<div class="col-sm-12 form-inline">
|
||||
<button type="button" class="btn btn-sm btn-primary pull-right"
|
||||
ng-click="ctrl.createKeyPair()">
|
||||
<span class="fa fa-fw fa-plus"></span>
|
||||
{$ ::ctrl.label.createKeyPair $}
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-primary pull-right"
|
||||
ng-click="ctrl.importKeyPair()">
|
||||
<span class="fa fa-fw fa-upload"></span>
|
||||
{$ ::ctrl.label.importKeyPair $}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Key Pairs Allocated-->
|
||||
<allocated>
|
||||
<table st-table="ctrl.tableData.displayedAllocated"
|
||||
st-safe-src="ctrl.tableData.allocated" hz-table
|
||||
class="table-striped table-rsp table-detail modern">
|
||||
<thead>
|
||||
<transfer-table tr-model="ctrl.tableData"
|
||||
help-text="ctrl.tableHelp"
|
||||
limits="ctrl.tableLimits">
|
||||
|
||||
<!-- Key Pairs Allocated-->
|
||||
<allocated>
|
||||
<table st-table="ctrl.tableData.displayedAllocated"
|
||||
st-safe-src="ctrl.tableData.allocated" hz-table
|
||||
class="table-striped table-rsp table-detail modern">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="expander"></th>
|
||||
<th class="rsp-p1">{$ ::ctrl.label.name $}</th>
|
||||
<th class="rsp-p2">{$ ::ctrl.tableLabels.fingerprint $}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-if="ctrl.tableData.allocated.length === 0">
|
||||
<td colspan="8">
|
||||
<div class="no-rows-help">
|
||||
{$ ::trCtrl.helpText.noneAllocText $}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat-start="row in ctrl.tableData.displayedAllocated track by row.id">
|
||||
<td class="expander">
|
||||
<span class="fa fa-chevron-right" hz-expand-detail
|
||||
title="{$ ::trCtrl.helpText.expandDetailsText $}"></span>
|
||||
</td>
|
||||
<td class="rsp-p1">{$ row.name $}</td>
|
||||
<td class="rsp-p2">{$ row.fingerprint $}</td>
|
||||
<td class="action-col">
|
||||
<action-list>
|
||||
<action action-classes="'btn btn-sm btn-default'"
|
||||
callback="trCtrl.deallocate" item="row">
|
||||
<span class="fa fa-minus"></span>
|
||||
</action>
|
||||
</action-list>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat-end class="detail-row">
|
||||
<td></td>
|
||||
<td class="detail" colspan="3">
|
||||
<dl class="dl-horizontal" ng-include="ctrl.tableDetails">
|
||||
</dl>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</allocated>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-if="ctrl.tableData.allocated.length === 0">
|
||||
<td colspan="8">
|
||||
<div class="no-rows-help">
|
||||
{$ ::trCtrl.helpText.noneAllocText $}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat-start="row in ctrl.tableData.displayedAllocated track by row.id">
|
||||
<td class="expander">
|
||||
<span class="fa fa-chevron-right" hz-expand-detail
|
||||
title="{$ ::trCtrl.helpText.expandDetailsText $}"></span>
|
||||
</td>
|
||||
<td class="rsp-p1">{$ row.name $}</td>
|
||||
<td class="rsp-p2">{$ row.fingerprint $}</td>
|
||||
<td class="action-col">
|
||||
<action-list>
|
||||
<action action-classes="'btn btn-sm btn-default'"
|
||||
callback="trCtrl.deallocate" item="row">
|
||||
<span class="fa fa-minus"></span>
|
||||
</action>
|
||||
</action-list>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat-end class="detail-row">
|
||||
<td></td>
|
||||
<td class="detail" colspan="3">
|
||||
<dl class="dl-horizontal" ng-include="ctrl.tableDetails">
|
||||
</dl>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</allocated>
|
||||
|
||||
<!-- Key Pairs Available -->
|
||||
<available>
|
||||
<table st-table="ctrl.tableData.displayedAvailable"
|
||||
st-safe-src="ctrl.tableData.available"
|
||||
hz-table class="table-striped table-rsp table-detail modern">
|
||||
<thead>
|
||||
<!-- Key Pairs Available -->
|
||||
<available>
|
||||
<table st-table="ctrl.tableData.displayedAvailable"
|
||||
st-safe-src="ctrl.tableData.available"
|
||||
hz-table class="table-striped table-rsp table-detail modern">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="search-header" colspan="7">
|
||||
<search-bar group-classes="input-group-sm"
|
||||
icon-classes="fa-search">
|
||||
<search-bar group-classes="input-group-sm" icon-classes="fa-search">
|
||||
</search-bar>
|
||||
</th>
|
||||
</tr>
|
||||
@ -75,8 +89,8 @@
|
||||
<th st-sort="fingerprint" class="rsp-p1">{$ ::ctrl.tableLabels.fingerprint $}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-if="trCtrl.numDisplayedAvailable() === 0">
|
||||
<td colspan="8">
|
||||
<div class="no-rows-help">
|
||||
@ -87,15 +101,15 @@
|
||||
<tr ng-repeat-start="row in ctrl.tableData.displayedAvailable track by row.id"
|
||||
ng-if="!trCtrl.allocatedIds[row.id]">
|
||||
<td class="expander">
|
||||
<span class="fa fa-chevron-right" hz-expand-detail
|
||||
title="{$ ::trCtrl.helpText.expandDetailsText $}"></span>
|
||||
<span class="fa fa-chevron-right" hz-expand-detail
|
||||
title="{$ ::trCtrl.helpText.expandDetailsText $}"></span>
|
||||
</td>
|
||||
<td class="rsp-p1">{$ row.name$}</td>
|
||||
<td class="rsp-p1">{$ row.fingerprint $}</td>
|
||||
<td class="action-col">
|
||||
<action-list>
|
||||
<action action-classes="'btn btn-sm btn-default'"
|
||||
callback="trCtrl.allocate" item="row">
|
||||
callback="trCtrl.allocate" item="row">
|
||||
<span class="fa fa-plus"></span>
|
||||
</action>
|
||||
</action-list>
|
||||
@ -106,11 +120,11 @@
|
||||
<td class="detail" colspan="3" ng-include="ctrl.tableDetails">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</available>
|
||||
</tbody>
|
||||
</table>
|
||||
</available>
|
||||
|
||||
</transfer-table> <!-- End Key Pairs Table -->
|
||||
</transfer-table> <!-- End Key Pairs Table -->
|
||||
|
||||
</div> <!-- End Content -->
|
||||
</div> <!-- End Controller -->
|
||||
</div> <!-- End Controller -->
|
@ -3,7 +3,7 @@
|
||||
|
||||
var module = angular.module('hz.dashboard.launch-instance');
|
||||
|
||||
/**
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name hz.dashboard.launch-instance.LaunchInstanceKeypairCtrl
|
||||
* @description
|
||||
@ -11,70 +11,283 @@
|
||||
*/
|
||||
module.controller('LaunchInstanceKeypairCtrl', [
|
||||
'launchInstanceModel',
|
||||
function (launchInstanceModel) {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.label = {
|
||||
title: gettext('Key Pair'),
|
||||
subtitle: gettext('Select a key pair.'),
|
||||
name: gettext('Name'),
|
||||
description: gettext('Description')
|
||||
};
|
||||
|
||||
ctrl.tableLabels = {
|
||||
fingerprint: gettext('Fingerprint'),
|
||||
public_key: gettext('Public Key')
|
||||
};
|
||||
|
||||
ctrl.tableData = {
|
||||
available: launchInstanceModel.keypairs,
|
||||
allocated: launchInstanceModel.newInstanceSpec.key_pair,
|
||||
displayedAvailable: [],
|
||||
displayedAllocated: []
|
||||
};
|
||||
|
||||
ctrl.tableDetails =
|
||||
'/static/dashboard/launch-instance/keypair/keypair-details.html';
|
||||
|
||||
ctrl.tableHelp = {
|
||||
noneAllocText: gettext('Select a key pair from the available key pairs below.')
|
||||
};
|
||||
|
||||
ctrl.tableLimits = {
|
||||
maxAllocation: 1
|
||||
};
|
||||
|
||||
}
|
||||
'$modal',
|
||||
'dashboardBasePath',
|
||||
LaunchInstanceKeypairCtrl
|
||||
]);
|
||||
|
||||
function LaunchInstanceKeypairCtrl(launchInstanceModel,
|
||||
$modal,
|
||||
dashboardBasePath) {
|
||||
var ctrl = this;
|
||||
|
||||
/**
|
||||
ctrl.label = {
|
||||
title: gettext('Key Pair'),
|
||||
subtitle: gettext('A key pair allows you to SSH into your newly created instance. You may select an existing key pair, import a key pair, or generate a new key pair.'),
|
||||
name: gettext('Name'),
|
||||
description: gettext('Description'),
|
||||
createKeyPair: gettext('Create Key Pair'),
|
||||
importKeyPair: gettext('Import Key Pair')
|
||||
};
|
||||
|
||||
ctrl.tableLabels = {
|
||||
fingerprint: gettext('Fingerprint'),
|
||||
public_key: gettext('Public Key')
|
||||
};
|
||||
|
||||
ctrl.tableData = {
|
||||
available: launchInstanceModel.keypairs,
|
||||
allocated: launchInstanceModel.newInstanceSpec.key_pair,
|
||||
displayedAvailable: [],
|
||||
displayedAllocated: []
|
||||
};
|
||||
|
||||
ctrl.tableDetails = dashboardBasePath + 'launch-instance/keypair/keypair-details.html';
|
||||
|
||||
ctrl.tableHelp = {
|
||||
noneAllocText: gettext('Select a key pair from the available key pairs below.')
|
||||
};
|
||||
|
||||
ctrl.tableLimits = {
|
||||
maxAllocation: 1
|
||||
};
|
||||
|
||||
// Allocate the new key pair (after import or create)
|
||||
// if nothing is already allocated
|
||||
ctrl.allocateNewKeyPair = function(newKeyPair) {
|
||||
if (ctrl.tableData.allocated.length === 0) {
|
||||
ctrl.tableData.allocated.push(newKeyPair);
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.createKeyPair = function () {
|
||||
$modal.open({
|
||||
templateUrl: dashboardBasePath + 'launch-instance/keypair/create-keypair.html',
|
||||
controller: 'LaunchInstanceCreateKeyPairCtrl as ctrl',
|
||||
windowClass: 'modal-dialog-wizard',
|
||||
}).result.then(
|
||||
function (result) {
|
||||
// Nova doesn't set the id in the response so we will use
|
||||
// the name as the id. Name is the key used in URLs, etc.
|
||||
result.id = result.name;
|
||||
|
||||
$modal.open({
|
||||
templateUrl: dashboardBasePath + 'launch-instance/keypair/new-keypair.html',
|
||||
controller: 'LaunchInstanceNewKeyPairCtrl as ctrl',
|
||||
windowClass: 'modal-dialog-wizard',
|
||||
resolve: {
|
||||
keypair: function () {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
launchInstanceModel.keypairs.push(result);
|
||||
ctrl.allocateNewKeyPair(result);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
ctrl.importKeyPair = function () {
|
||||
$modal.open({
|
||||
templateUrl: dashboardBasePath + 'launch-instance/keypair/import-keypair.html',
|
||||
controller: 'LaunchInstanceImportKeyPairCtrl as ctrl',
|
||||
windowClass: 'modal-dialog-wizard'
|
||||
}).result.then(
|
||||
function (result) {
|
||||
// Nova doesn't set the id in the response so we will use
|
||||
// the name as the id. Name is the key used in URLs, etc.
|
||||
result.id = result.name;
|
||||
|
||||
launchInstanceModel.keypairs.push(result);
|
||||
ctrl.allocateNewKeyPair(result);
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name hz.dashboard.launch-instance.LaunchInstanceKeypairHelpCtrl
|
||||
* @description
|
||||
* Provide help for selection of security groups and key pairs.
|
||||
* Provide help for selection of a key pair.
|
||||
*/
|
||||
module.controller('LaunchInstanceKeypairHelpCtrl', [function () {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.title = gettext('Key Pair Help');
|
||||
|
||||
var genKeyPairsMap = { genKeyPairCmd: 'ssh-keygen' };
|
||||
var genKeyPairsText = gettext('There are two ways to generate a key pair. From a Linux system, generate the key pair with the <samp>%(genKeyPairCmd)s</samp> command:');
|
||||
|
||||
var keyPathsMap = { privateKeyPath: 'cloud.key', publicKeyPath: 'cloud.key.pub' };
|
||||
var keyPathText = gettext('This command generates a pair of keys: a private key (%(privateKeyPath)s) and a public key (%(publicKeyPath)s).');
|
||||
|
||||
var windowsCmdMap = { authorizeKeysFile: '.ssh/authorized_keys' };
|
||||
var windowsCmd = gettext('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>%(authorizeKeysFile)s</samp> file.');
|
||||
|
||||
ctrl.paragraphs = [
|
||||
gettext('The key pair allows you to SSH into the instance.'),
|
||||
interpolate(genKeyPairsText, genKeyPairsMap, true),
|
||||
'<samp>ssh-keygen -t rsa -f cloud.key</samp>',
|
||||
interpolate(keyPathText, keyPathsMap, true),
|
||||
interpolate(windowsCmd, windowsCmdMap, true)
|
||||
];
|
||||
}
|
||||
module.controller('LaunchInstanceKeypairHelpCtrl', [
|
||||
LaunchInstanceKeypairHelpCtrl
|
||||
]);
|
||||
|
||||
function LaunchInstanceKeypairHelpCtrl() {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.title = gettext('Key Pair Help');
|
||||
|
||||
var genKeyPairsMap = {genKeyPairCmd: 'ssh-keygen'};
|
||||
var genKeyPairsText = gettext('There are two ways to generate a key pair. From a Linux system, generate the key pair with the <samp>%(genKeyPairCmd)s</samp> command:');
|
||||
|
||||
var keyPathsMap = {
|
||||
privateKeyPath: 'cloud.key',
|
||||
publicKeyPath: 'cloud.key.pub'
|
||||
};
|
||||
var keyPathText = gettext('This command generates a pair of keys: a private key (%(privateKeyPath)s) and a public key (%(publicKeyPath)s).');
|
||||
|
||||
var windowsCmdMap = {authorizeKeysFile: '.ssh/authorized_keys'};
|
||||
var windowsCmd = gettext('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>%(authorizeKeysFile)s</samp> file.');
|
||||
|
||||
ctrl.paragraphs = [
|
||||
gettext('The key pair allows you to SSH into the instance.'),
|
||||
interpolate(genKeyPairsText, genKeyPairsMap, true),
|
||||
'<samp>ssh-keygen -t rsa -f cloud.key</samp>',
|
||||
interpolate(keyPathText, keyPathsMap, true),
|
||||
interpolate(windowsCmd, windowsCmdMap, true)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name hz.dashboard.launch-instance.LaunchInstanceCreateKeyPairCtrl
|
||||
* @description
|
||||
* Provide a dialog for creation of a new key pair.
|
||||
*/
|
||||
module.controller('LaunchInstanceCreateKeyPairCtrl', [
|
||||
'$modalInstance',
|
||||
'novaAPI',
|
||||
LaunchInstanceCreateKeyPairCtrl
|
||||
]);
|
||||
|
||||
function LaunchInstanceCreateKeyPairCtrl($modalInstance, novaAPI) {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.labels = {
|
||||
wizardTitle: gettext('Launch Instance'),
|
||||
title: gettext('Create Key Pair'),
|
||||
help: gettext('Key Pairs are how you login to your instance after it is launched. Choose a key pair name you will recognize.'),
|
||||
keyPairName: gettext('Key Pair Name'),
|
||||
cancel: gettext('Cancel'),
|
||||
ok: gettext('Create Key Pair'),
|
||||
required: gettext('Required')
|
||||
};
|
||||
|
||||
ctrl.model = { name: '' };
|
||||
ctrl.submit = function () {
|
||||
novaAPI.createKeypair(ctrl.model)
|
||||
.success(function (data) {
|
||||
$modalInstance.close(data);
|
||||
|
||||
var successMsg = gettext('Successfully created key pair %(name)s');
|
||||
horizon.alert('success', interpolate(successMsg, { name: data.name }, true));
|
||||
});
|
||||
};
|
||||
ctrl.cancel = function () {
|
||||
$modalInstance.dismiss();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name hz.dashboard.launch-instance.LaunchInstanceNewKeyPairCtrl
|
||||
* @description
|
||||
* Provide a dialog for display of the information about a new
|
||||
* public/private key pair.
|
||||
*/
|
||||
module.controller('LaunchInstanceNewKeyPairCtrl', [
|
||||
'$modalInstance',
|
||||
'keypair',
|
||||
LaunchInstanceNewKeyPairCtrl
|
||||
]);
|
||||
|
||||
function LaunchInstanceNewKeyPairCtrl($modalInstance, keypair) {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.labels = {
|
||||
wizardTitle: gettext('Launch Instance'),
|
||||
title: gettext('Private Key'),
|
||||
help: gettext('This is your new key pair. Copy this information and keep it secure.'),
|
||||
keyPairName: gettext('Key Pair Name'),
|
||||
fingerprint: gettext('Fingerprint'),
|
||||
privateKey: gettext('Private Key'),
|
||||
publicKey: gettext('Public Key'),
|
||||
ok: gettext('OK')
|
||||
};
|
||||
|
||||
ctrl.keypair = keypair;
|
||||
ctrl.ok = function () {
|
||||
$modalInstance.dismiss();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name hz.dashboard.launch-instance.LaunchInstanceImportKeyPairCtrl
|
||||
* @description
|
||||
* Provide a dialog for import of an existing ssh public key.
|
||||
*/
|
||||
module.controller('LaunchInstanceImportKeyPairCtrl', [
|
||||
'$modalInstance',
|
||||
'novaAPI',
|
||||
LaunchInstanceImportKeyPairCtrl
|
||||
]);
|
||||
|
||||
function LaunchInstanceImportKeyPairCtrl($modalInstance, novaAPI) {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.labels = {
|
||||
wizardTitle: gettext('Launch Instance'),
|
||||
title: gettext('Import Key Pair'),
|
||||
help: gettext('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.'),
|
||||
keyPairName: gettext('Key Pair Name'),
|
||||
publicKey: gettext('Public Key'),
|
||||
cancel: gettext('Cancel'),
|
||||
ok: gettext('Import Key Pair'),
|
||||
required: gettext('Required')
|
||||
};
|
||||
|
||||
ctrl.model = { name: '', public_key: '' };
|
||||
ctrl.submit = function () {
|
||||
novaAPI.createKeypair(ctrl.model)
|
||||
.success(function (data) {
|
||||
$modalInstance.close(data);
|
||||
|
||||
var successMsg = gettext('Successfully imported key pair %(name)s');
|
||||
horizon.alert('success', interpolate(successMsg, { name: data.name }, true));
|
||||
});
|
||||
};
|
||||
ctrl.cancel = function () {
|
||||
$modalInstance.dismiss();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name LaunchInstanceImportKeyPairHelpCtrl
|
||||
* @description
|
||||
* The `LaunchInstanceImportKeyPairHelpCtrl` controller provides help text
|
||||
* for the import key pair function within the Launch Instance Wizard.
|
||||
*
|
||||
*/
|
||||
module.controller('LaunchInstanceImportKeyPairHelpCtrl', [
|
||||
LaunchInstanceImportKeyPairHelpCtrl
|
||||
]);
|
||||
|
||||
function LaunchInstanceImportKeyPairHelpCtrl() {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.title = gettext('Import Key Pair Help');
|
||||
|
||||
var genKeyPairsMap = { genKeyPairCmd: 'ssh-keygen' };
|
||||
var genKeyPairsText = gettext('There are two ways to generate a key pair. From a Linux system, generate the key pair with the <samp>%(genKeyPairCmd)s</samp> command:');
|
||||
|
||||
var keyPathsMap = { privateKeyPath: 'cloud.key', publicKeyPath: 'cloud.key.pub' };
|
||||
var keyPathText = gettext('This command generates a pair of keys: a private key (%(privateKeyPath)s) and a public key (%(publicKeyPath)s).');
|
||||
|
||||
var windowsCmdMap = { authorizeKeysFile: '.ssh/authorized_keys' };
|
||||
var windowsCmd = gettext('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>%(authorizeKeysFile)s</samp> file.');
|
||||
|
||||
ctrl.paragraphs = [
|
||||
interpolate(genKeyPairsText, genKeyPairsMap, true),
|
||||
'<samp>ssh-keygen -t rsa -f cloud.key</samp>',
|
||||
interpolate(keyPathText, keyPathsMap, true),
|
||||
interpolate(windowsCmd, windowsCmdMap, true)
|
||||
];
|
||||
}
|
||||
|
||||
})();
|
||||
|
@ -15,4 +15,19 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-family: $code-font-family;
|
||||
}
|
||||
}
|
||||
|
||||
.no-navigation {
|
||||
|
||||
.step {
|
||||
left: 25px;
|
||||
}
|
||||
|
||||
.form-field {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
}
|
||||
|
@ -17,16 +17,26 @@
|
||||
'use strict';
|
||||
|
||||
describe('Launch Instance Keypair Step', function() {
|
||||
|
||||
describe('LaunchInstanceKeypairCtrl', function() {
|
||||
var ctrl;
|
||||
|
||||
beforeEach(module('hz.dashboard.launch-instance'));
|
||||
beforeEach(module(function ($provide) {
|
||||
$provide.value('$modal', {});
|
||||
}));
|
||||
|
||||
beforeEach(module('hz.dashboard'));
|
||||
|
||||
beforeEach(inject(function($controller) {
|
||||
var model = { newInstanceSpec: { key_pair: ['key1'] },
|
||||
keypairs: ['key1', 'key2'] };
|
||||
var model = {
|
||||
newInstanceSpec: {
|
||||
key_pair: ['key1']
|
||||
},
|
||||
keypairs: ['key1', 'key2']
|
||||
};
|
||||
|
||||
ctrl = $controller('LaunchInstanceKeypairCtrl',
|
||||
{launchInstanceModel: model});
|
||||
{ launchInstanceModel: model });
|
||||
}));
|
||||
|
||||
it('contains its general labels', function() {
|
||||
@ -67,13 +77,12 @@
|
||||
});
|
||||
|
||||
describe('LaunchInstanceKeypairHelpCtrl', function() {
|
||||
var scope, ctrl;
|
||||
var ctrl;
|
||||
|
||||
beforeEach(module('hz.dashboard.launch-instance'));
|
||||
|
||||
beforeEach(inject(function($controller) {
|
||||
scope = {};
|
||||
ctrl = $controller('LaunchInstanceKeypairHelpCtrl', {$scope:scope});
|
||||
ctrl = $controller('LaunchInstanceKeypairHelpCtrl');
|
||||
}));
|
||||
|
||||
it('defines the title', function() {
|
||||
@ -86,6 +95,115 @@
|
||||
});
|
||||
});
|
||||
|
||||
describe('LaunchInstanceCreateKeyPairCtrl', function() {
|
||||
var ctrl;
|
||||
|
||||
beforeEach(module(function ($provide) {
|
||||
$provide.value('$modalInstance', {});
|
||||
$provide.value('novaAPI', {});
|
||||
}));
|
||||
|
||||
beforeEach(module('hz.dashboard'));
|
||||
|
||||
beforeEach(inject(function($controller) {
|
||||
ctrl = $controller('LaunchInstanceCreateKeyPairCtrl');
|
||||
}));
|
||||
|
||||
it('contains its general labels', function() {
|
||||
expect(ctrl.labels).toBeDefined();
|
||||
});
|
||||
|
||||
it('defines a model with a empty name', function() {
|
||||
expect(ctrl.model).toBeDefined();
|
||||
expect(ctrl.model.name).toBe('');
|
||||
});
|
||||
|
||||
it('defines a submit function', function() {
|
||||
expect(ctrl.submit).toBeDefined();
|
||||
});
|
||||
|
||||
it('defines a cancel function', function() {
|
||||
expect(ctrl.cancel).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('LaunchInstanceNewKeyPairCtrl', function() {
|
||||
var ctrl;
|
||||
|
||||
beforeEach(module(function ($provide) {
|
||||
$provide.value('$modalInstance', {});
|
||||
}));
|
||||
|
||||
beforeEach(module('hz.dashboard'));
|
||||
|
||||
beforeEach(inject(function($controller) {
|
||||
ctrl = $controller('LaunchInstanceNewKeyPairCtrl', { keypair: {} });
|
||||
}));
|
||||
|
||||
it('contains its general labels', function() {
|
||||
expect(ctrl.labels).toBeDefined();
|
||||
});
|
||||
|
||||
it('defines an empty keypair', function() {
|
||||
expect(ctrl.keypair).toBeDefined();
|
||||
});
|
||||
|
||||
it('defines an OK function', function() {
|
||||
expect(ctrl.ok).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('LaunchInstanceImportKeyPairCtrl', function() {
|
||||
var ctrl;
|
||||
|
||||
beforeEach(module(function ($provide) {
|
||||
$provide.value('$modalInstance', {});
|
||||
$provide.value('novaAPI', {});
|
||||
}));
|
||||
|
||||
beforeEach(module('hz.dashboard'));
|
||||
|
||||
beforeEach(inject(function($controller) {
|
||||
ctrl = $controller('LaunchInstanceImportKeyPairCtrl');
|
||||
}));
|
||||
|
||||
it('contains its general labels', function() {
|
||||
expect(ctrl.labels).toBeDefined();
|
||||
});
|
||||
|
||||
it('defines a model with a empty name and public key', function() {
|
||||
expect(ctrl.model).toBeDefined();
|
||||
expect(ctrl.model.name).toBe('');
|
||||
expect(ctrl.model.public_key).toBe('');
|
||||
});
|
||||
|
||||
it('defines a submit function', function() {
|
||||
expect(ctrl.submit).toBeDefined();
|
||||
});
|
||||
|
||||
it('defines a cancel function', function() {
|
||||
expect(ctrl.cancel).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('LaunchInstanceImportKeyPairHelpCtrl', function() {
|
||||
var ctrl;
|
||||
|
||||
beforeEach(module('hz.dashboard.launch-instance'));
|
||||
|
||||
beforeEach(inject(function($controller) {
|
||||
ctrl = $controller('LaunchInstanceImportKeyPairHelpCtrl');
|
||||
}));
|
||||
|
||||
it('defines the title', function() {
|
||||
expect(ctrl.title).toBeDefined();
|
||||
});
|
||||
|
||||
it('has paragraphs', function() {
|
||||
expect(ctrl.paragraphs).toBeDefined();
|
||||
expect(ctrl.paragraphs.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
@ -0,0 +1,31 @@
|
||||
<div class="ng-wizard no-navigation">
|
||||
<div class="title" ng-bind="::ctrl.labels.wizardTitle"></div>
|
||||
|
||||
<div class="step">
|
||||
<h1>{$ ::ctrl.labels.title $}</h1>
|
||||
<div class="content">
|
||||
<div class="subtitle">{$ ::ctrl.labels.help $}</div>
|
||||
|
||||
<dl class="dl-horizontal key-pair-details">
|
||||
<dt>{$ ::ctrl.labels.keyPairName $}</dt>
|
||||
<dd><pre><code>{$ ctrl.keypair.name $}</code></pre></dd>
|
||||
<dt>{$ ::ctrl.labels.fingerprint $}</dt>
|
||||
<dd><pre><code>{$ ctrl.keypair.fingerprint $}</code></pre></dd>
|
||||
<dt>{$ ::ctrl.labels.publicKey $}</dt>
|
||||
<dd><pre><code>{$ ctrl.keypair.public_key $}</code></pre></dd>
|
||||
<dt>{$ ::ctrl.labels.privateKey $}</dt>
|
||||
<dd><pre><code>{$ ctrl.keypair.private_key $}</code></pre></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toolbar">
|
||||
<div class="primary-btn-grp">
|
||||
<button class="finish btn btn-sm btn-success"
|
||||
ng-click="ctrl.ok()">
|
||||
<span class="fa fa-check"></span>
|
||||
{$ ::ctrl.labels.ok $}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -3,7 +3,7 @@
|
||||
* check out http://www.sass-lang.com/guide#topic-6
|
||||
*/
|
||||
|
||||
@mixin search-placeholder {
|
||||
@mixin input-placeholder {
|
||||
&::-webkit-input-placeholder {
|
||||
@content;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user