Merge "Add Manage Security Groups action for container"

This commit is contained in:
Zuul 2018-04-19 05:20:27 +00:00 committed by Gerrit Code Review
commit 4d7dcaa3dd
14 changed files with 707 additions and 97 deletions

View File

@ -0,0 +1,6 @@
---
features:
- >
[`manage-security-groups <https://blueprints.launchpad.net/zun-ui/+spec/manage-security-groups>`_]
Added Manage Security Groups action to manage associations between
security groups and ports on container.

View File

@ -10,16 +10,19 @@
# License for the specific language governing permissions and limitations
# under the License.
import logging
from django.conf import settings
from horizon import exceptions
from horizon.utils.memoized import memoized_with_request
import logging
from openstack_dashboard.api import base
from neutronclient.v2_0 import client as neutron_client
from zunclient import api_versions
from zunclient.common import utils
from zunclient.v1 import client as zun_client
LOG = logging.getLogger(__name__)
CONTAINER_CREATE_ATTRS = zun_client.containers.CREATION_ATTRIBUTES
@ -65,6 +68,33 @@ def zunclient(request_auth_params):
return c
def get_auth_params_from_request_neutron(request):
"""Extracts properties needed by neutronclient call from the request object.
These will be used to memoize the calls to neutronclient.
"""
return (
request.user.token.id,
base.url_for(request, 'network'),
base.url_for(request, 'identity')
)
@memoized_with_request(get_auth_params_from_request_neutron)
def neutronclient(request_auth_params):
token_id, neutron_url, auth_url = request_auth_params
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
LOG.debug('neutronclient connection created using the token "%s" and url'
' "%s"' % (token_id, neutron_url))
c = neutron_client.Client(token=token_id,
auth_url=auth_url,
endpoint_url=neutron_url,
insecure=insecure, ca_cert=cacert)
return c
def _cleanup_params(attrs, check, **params):
args = {}
run = False
@ -229,6 +259,14 @@ def container_network_detach(request, id):
return {"container": id, "network": network}
def port_update_security_groups(request):
port = request.DATA.get("port") or None
security_groups = request.DATA.get("security_groups") or None
kwargs = {"security_groups": security_groups}
neutronclient(request).update_port(port, body={"port": kwargs})
return {"port": port, "security_group": security_groups}
def image_list(request, limit=None, marker=None, sort_key=None,
sort_dir=None, detail=True):
return zunclient(request).images.list(limit, marker, sort_key,

View File

@ -90,6 +90,8 @@ class ContainerActions(generic.View):
return client.container_network_attach(request, id)
elif action == 'network_detach':
return client.container_network_detach(request, id)
elif action == 'port_update_security_groups':
return client.port_update_security_groups(request)
@rest_utils.ajax(data_required=True)
def delete(self, request, id, action):

View File

@ -45,6 +45,7 @@
'horizon.dashboard.container.containers.execute.service',
'horizon.dashboard.container.containers.kill.service',
'horizon.dashboard.container.containers.refresh.service',
'horizon.dashboard.container.containers.manage-security-groups.service',
'horizon.dashboard.container.containers.resourceType'
];
@ -64,6 +65,7 @@
executeContainerService,
killContainerService,
refreshContainerService,
manageSecurityGroupService,
resourceType
) {
var containersResourceType = registry.getResourceType(resourceType);
@ -103,6 +105,13 @@
text: gettext('Update Container')
}
})
.append({
id: 'manageSecurityGroupService',
service: manageSecurityGroupService,
template: {
text: gettext('Manage Security Groups')
}
})
.append({
id: 'startContainerAction',
service: startContainerService,

View File

@ -89,6 +89,8 @@
delete context.model.ports;
delete context.model.availableNetworks;
delete context.model.allocatedNetworks;
delete context.model.availableSecurityGroups;
delete context.model.allocatedSecurityGroups;
delete context.model.availablePorts;
context.model.hints = setSchedulerHints(context.model);
delete context.model.availableHints;

View File

@ -0,0 +1,60 @@
/*
* 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.dashboard.container.containers.manage-security-groups.delete.service
* @description Service for the security group deletion for manage-security-groups modal
*/
angular
.module('horizon.dashboard.container.containers')
.constant('horizon.dashboard.container.containers.manage-security-groups.delete.events',
events())
.factory('horizon.dashboard.container.containers.manage-security-groups.delete.service',
service);
function events() {
return {
DELETE_SUCCESS: 'horizon.dashboard.container.containers.manage-security-groups.DELETE_SUCCESS'
};
}
service.$inject = [
'horizon.framework.util.q.extensions',
'horizon.dashboard.container.containers.manage-security-groups.delete.events'
];
function service(
$qExtensions,
events
) {
var service = {
allowed: allowed,
perform: perform
};
return service;
function allowed() {
return $qExtensions.booleanAsPromise(true);
}
function perform(selected, scope) {
scope.$emit(events.DELETE_SUCCESS, selected);
return $qExtensions.booleanAsPromise(true);
}
}
})();

View File

@ -0,0 +1,261 @@
/*
* 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.dashboard.container.containers.manage-security-groups
* @description
* Controller for the Manage Security Groups dialog.
*/
angular
.module('horizon.dashboard.container.containers')
.controller('horizon.dashboard.container.containers.manage-security-groups',
ManageSecurityGroupsController);
ManageSecurityGroupsController.$inject = [
'$scope',
'horizon.app.core.openstack-service-api.neutron',
'horizon.app.core.openstack-service-api.security-group',
'horizon.dashboard.container.containers.manage-security-groups.delete.service',
'horizon.dashboard.container.containers.manage-security-groups.delete.events'
];
function ManageSecurityGroupsController(
$scope, neutron, securityGroup, deleteSecurityGroupService, events
) {
var ctrl = this;
// form settings to add association of port and security group into table ///////////
// model template
ctrl.modelTemplate = {
id: "",
port: "",
port_name: "",
security_group: "",
security_group_name: ""
};
// initiate model
ctrl.model = angular.copy(ctrl.modelTemplate);
// for port selection
ctrl.availablePorts = [
{id: "", name: gettext("Select port.")}
];
// for security group selection
var message = {
portNotSelected: gettext("Select port first."),
portSelected: gettext("Select security group.")
};
ctrl.availableSecurityGroups = [
{id: "", name: message.portNotSelected, selected: false}
];
ctrl.refreshAvailableSecurityGroups = refreshAvailableSecurityGroups;
// add association into table
ctrl.addSecurityGroup = function(event) {
ctrl.model.id = ctrl.model.port + " " + ctrl.model.security_group;
ctrl.model.port_name = getPortName(ctrl.model.port);
ctrl.model.security_group_name = getSecurityGroupName(ctrl.model.security_group);
var model = angular.copy(ctrl.model);
$scope.model.port_security_groups.push(model);
// clean up form
ctrl.model = angular.copy(ctrl.modelTemplate);
ctrl.disabledSecurityGroup = true;
event.stopPropagation();
event.preventDefault();
refreshAvailableSecurityGroups();
};
// get port name from available ports.
function getPortName(port) {
var result = "";
ctrl.availablePorts.forEach(function (ap) {
if (port === ap.id) {
result = ap.name;
}
});
return result;
}
// get security group name from available security groups.
function getSecurityGroupName(sg) {
var result = "";
ctrl.availableSecurityGroups.forEach(function (asg) {
if (sg === asg.id) {
result = asg.name;
}
});
return result;
}
// refresh available security group selection, according to addition/deletion of associations.
ctrl.disabledSecurityGroup = true;
function refreshAvailableSecurityGroups() {
if (ctrl.model.port) {
// if port is selected, enable port selection.
ctrl.disabledSecurityGroup = false;
} else {
// otherwise disable port selection.
ctrl.disabledSecurityGroup = true;
}
// set "selected" to true, if the security group already added into table.
ctrl.availableSecurityGroups.forEach(function (sg) {
sg.selected = false;
ctrl.items.forEach(function (item) {
if (sg.id === item.security_group && ctrl.model.port === item.port) {
// mark already selected
sg.selected = true;
}
});
});
}
// enable "Add Security Group" button, if both of port and security group are selected.
ctrl.validateSecurityGroup = function () {
return !(ctrl.model.port && ctrl.model.security_group);
};
// retrieve available ports and security groups ///////////////////////////
// get security groups first, then get networks
securityGroup.query().then(onGetSecurityGroups).then(getNetworks);
function onGetSecurityGroups(response) {
angular.forEach(response.data.items, function (item) {
ctrl.availableSecurityGroups.push({id: item.id, name: item.name, selected: false});
// if association of port and security group in $scope.model.port_security_groups,
// push it into table for update.
if ($scope.model.port_security_groups.includes(item.id)) {
ctrl.security_groups.push(item);
}
});
return response;
}
// get available neutron networks and ports
function getNetworks() {
return neutron.getNetworks().then(onGetNetworks).then(getPorts);
}
function onGetNetworks(response) {
return response;
}
function getPorts(networks) {
networks.data.items.forEach(function(network) {
return neutron.getPorts({network_id: network.id}).then(
function(ports) {
onGetPorts(ports, network);
}
);
});
return networks;
}
function onGetPorts(ports, network) {
ports.data.items.forEach(function(port) {
// no device_owner or compute:kuryr means that the port can be associated
// with security group
if ((port.device_owner === "" || port.device_owner === "compute:kuryr") &&
port.admin_state === "UP") {
port.subnet_names = getPortSubnets(port, network.subnets);
port.network_name = network.name;
if ($scope.model.ports.includes(port.id)) {
var portName = port.network_name + " - " + port.subnet_names + " - " + port.name;
ctrl.availablePorts.push({
id: port.id,
name: portName});
port.security_groups.forEach(function (sgId) {
var sgName;
ctrl.availableSecurityGroups.forEach(function (sg) {
if (sgId === sg.id) {
sgName = sg.name;
}
});
$scope.model.port_security_groups.push({
id: port.id + " " + sgId,
port: port.id,
port_name: portName,
security_group: sgId,
security_group_name: sgName
});
});
}
}
});
}
// helper function to return an object of IP:NAME pairs for subnet mapping
function getPortSubnets(port, subnets) {
//var subnetNames = {};
var subnetNames = "";
port.fixed_ips.forEach(function (ip) {
subnets.forEach(function (subnet) {
if (ip.subnet_id === subnet.id) {
//subnetNames[ip.ip_address] = subnet.name;
if (subnetNames) {
subnetNames += ", ";
}
subnetNames += ip.ip_address + ": " + subnet.name;
}
});
});
return subnetNames;
}
// settings for table of added security groups ////////////////////////////
ctrl.items = $scope.model.port_security_groups;
ctrl.config = {
selectAll: false,
expand: false,
trackId: 'id',
columns: [
{id: 'port_name', title: gettext('Port')},
{id: 'security_group_name', title: gettext('Security Group')}
]
};
ctrl.itemActions = [
{
id: 'deleteSecurityGroupAction',
service: deleteSecurityGroupService,
template: {
type: "delete",
text: gettext("Delete")
}
}
];
// register watcher for security group deletion from table
var deleteWatcher = $scope.$on(events.DELETE_SUCCESS, deleteSecurityGroup);
$scope.$on('$destroy', function destroy() {
deleteWatcher();
});
// on delete security group from table
function deleteSecurityGroup(event, deleted) {
// delete security group from table
ctrl.items.forEach(function (sg, index) {
if (sg.id === deleted.port + " " + deleted.security_group) {
delete ctrl.items.splice(index, 1);
}
});
// enable deleted security group in selection
refreshAvailableSecurityGroups();
}
}
})();

View File

@ -0,0 +1,43 @@
<div ng-controller="horizon.dashboard.container.containers.manage-security-groups as ctrl">
<p class="step-description" translate>
Manage security group for ports.
</p>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label" for="port" translate>Port</label>
<select class="form-control" id="port" name="port"
ng-model="ctrl.model.port"
ng-options="port.id as port.name for port in ctrl.availablePorts"
ng-change="ctrl.refreshAvailableSecurityGroups()">
</select>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="control-label" for="security_group" translate>Security Group</label>
<select
class="form-control" id="security_group" name="security_group"
ng-model="ctrl.model.security_group"
ng-options="sg.id as sg.name for sg in ctrl.availableSecurityGroups | filter:{selected: false}"
ng-disabled="ctrl.disabledSecurityGroup">
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary"
ng-disabled="ctrl.validateSecurityGroup()"
ng-click="ctrl.addSecurityGroup($event)">
<span class="fa fa-arrow-down"></span>
<translate>Add Security Group</translate>
</button>
</div>
<hz-dynamic-table
config="ctrl.config"
items="ctrl.items"
item-actions="ctrl.itemActions">
</hz-dynamic-table>
</div>

View File

@ -0,0 +1,151 @@
/**
* 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";
angular
.module("horizon.dashboard.container.containers")
.factory("horizon.dashboard.container.containers.manage-security-groups.service",
manageSecurityGroup);
manageSecurityGroup.$inject = [
"horizon.app.core.openstack-service-api.neutron",
"horizon.app.core.openstack-service-api.security-group",
"horizon.app.core.openstack-service-api.zun",
"horizon.dashboard.container.basePath",
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.validStates',
'horizon.framework.util.actions.action-result.service',
"horizon.framework.util.i18n.gettext",
"horizon.framework.util.q.extensions",
"horizon.framework.widgets.form.ModalFormService",
"horizon.framework.widgets.toast.service"
];
function manageSecurityGroup(
neutron, securityGroup, zun, basePath, resourceType, validStates, actionResult,
gettext, $qExtensions, modal, toast
) {
// title for dialog
var title = gettext("Manage Security Groups: container %s");
// schema
var schema = {
type: "object",
properties: {
signal: {
title: gettext("Manage Security Groups"),
type: "string"
}
}
};
// form
var form = [
{
type: 'section',
htmlClass: 'row',
items: [
{
type: "section",
htmlClass: "col-xs-12",
items: [
{
type: "template",
/* eslint-disable max-len */
templateUrl: basePath + "containers/actions/manage-security-groups/manage-security-groups.html"
}
]
}
]
}
];
// model
var model = {};
var message = {
success: gettext("Changes security groups %(sgs)s for port %(port)s was successfully submitted.")
};
var service = {
initAction: initAction,
perform: perform,
allowed: allowed
};
return service;
//////////////
function initAction() {
}
function allowed(container) {
return $qExtensions.booleanAsPromise(
validStates.manage_security_groups.indexOf(container.status) >= 0
);
}
function perform(selected) {
model.id = selected.id;
model.name = selected.name;
model.ports = [];
Object.keys(selected.addresses).map(function(key) {
selected.addresses[key].forEach(function (addr) {
model.ports.push(addr.port);
});
});
model.port_security_groups = [];
// modal config
var config = {
"title": interpolate(title, [model.name]),
"submitText": gettext("Submit"),
"schema": schema,
"form": form,
"model": model
};
return modal.open(config).then(submit);
}
function submit(context) {
var id = context.model.id;
var portSgs = context.model.port_security_groups;
var aggregatedPortSgs = {};
// initialize port list
model.ports.forEach(function (port) {
aggregatedPortSgs[port] = [];
});
// add security groups for each port
portSgs.forEach(function (portSg) {
aggregatedPortSgs[portSg.port].push(portSg.security_group);
});
Object.keys(aggregatedPortSgs).map(function (port) {
return zun.updatePortSecurityGroup(id, port, aggregatedPortSgs[port]).then(function() {
var sgs = gettext("(empty)");
if (aggregatedPortSgs[port].length) {
sgs = aggregatedPortSgs[port];
}
toast.add(
'success',
interpolate(message.success, {port: port, sgs: sgs}, true));
var result = actionResult.getActionResult().updated(resourceType, id);
return result.result;
});
});
}
}
})();

View File

@ -65,52 +65,7 @@
var title, submitText;
title = gettext('Update Container');
submitText = gettext('Update');
var config = workflow.init('update', title, submitText);
config.model.id = selected.id;
// load current data
zun.getContainer(selected.id).then(onLoad);
function onLoad(response) {
config.model.name = response.data.name
? response.data.name : "";
config.model.image = response.data.image
? response.data.image : "";
config.model.image_driver = response.data.image_driver
? response.data.image_driver : "docker";
config.model.image_pull_policy = response.data.image_pull_policy
? response.data.image_pull_policy : "";
config.model.command = response.data.command
? response.data.command : "";
config.model.hostname = response.data.hostname
? response.data.hostname : "";
config.model.auto_remove = response.data.auto_remove
? response.data.auto_remove : false;
config.model.cpu = response.data.cpu
? response.data.cpu : "";
config.model.memory = response.data.memory
? parseInt(response.data.memory, 10) : "";
config.model.restart_policy = response.data.restart_policy.Name
? response.data.restart_policy.Name : "";
config.model.restart_policy_max_retry = response.data.restart_policy.MaximumRetryCount
? parseInt(response.data.restart_policy.MaximumRetryCount, 10) : null;
if (config.model.auto_remove) {
config.model.exit_policy = "remove";
} else {
config.model.exit_policy = config.model.restart_policy;
}
config.model.runtime = response.data.runtime
? response.data.runtime : "";
config.model.allocatedNetworks = getAllocatedNetworks(response.data.addresses);
config.model.workdir = response.data.workdir
? response.data.workdir : "";
config.model.environment = response.data.environment
? hashToString(response.data.environment) : "";
config.model.interactive = response.data.interactive
? response.data.interactive : false;
config.model.labels = response.data.labels
? hashToString(response.data.labels) : "";
}
var config = workflow.init('update', title, submitText, selected.id);
return modal.open(config).then(submit);
}
@ -183,37 +138,16 @@
function successAttach(response) {
toast.add('success', interpolate(message.successAttach,
[response.data.container, response.data.network]));
[response.data.network, response.data.container]));
var result = actionResult.getActionResult().updated(resourceType, response.data.container);
return result.result;
}
function successDetach(response) {
toast.add('success', interpolate(message.successDetach,
[response.data.container, response.data.network]));
[response.data.network, response.data.container]));
var result = actionResult.getActionResult().updated(resourceType, response.data.container);
return result.result;
}
function getAllocatedNetworks(addresses) {
var allocated = [];
Object.keys(addresses).forEach(function (id) {
allocated.push(id);
});
return allocated;
}
function hashToString(hash) {
var str = "";
for (var key in hash) {
if (hash.hasOwnProperty(key)) {
if (str.length > 0) {
str += ",";
}
str += key + "=" + hash[key];
}
}
return str;
}
}
})();

View File

@ -21,7 +21,6 @@
SecurityGroupsController.$inject = [
'$scope',
'horizon.app.core.openstack-service-api.security-group',
'horizon.dashboard.container.basePath'
];
@ -34,15 +33,11 @@
* Allows selection of security groups.
* @returns {undefined} No return value
*/
function SecurityGroupsController($scope, securityGroup, basePath) {
var push = Array.prototype.push;
function SecurityGroupsController($scope, basePath) {
var ctrl = this;
var availableSecurityGroups = [];
securityGroup.query().then(onGetSecurityGroups);
ctrl.tableData = {
available: availableSecurityGroups,
available: $scope.model.availableSecurityGroups,
allocated: $scope.model.security_groups,
displayedAvailable: [],
displayedAllocated: []
@ -75,19 +70,5 @@
singleton: true
}
];
// Security Groups
function onGetSecurityGroups(data) {
angular.forEach(data.data.items, function addDefault(item) {
// 'default' is a special security group in neutron. It can not be
// deleted and is guaranteed to exist. It by default contains all
// of the rules needed for an instance to reach out to the network
// so the instance can provision itself.
if (item.name === 'default') {
$scope.model.security_groups.push(item);
}
});
push.apply(availableSecurityGroups, data.data.items);
}
}
})();

View File

@ -22,17 +22,23 @@
workflow.$inject = [
"horizon.app.core.openstack-service-api.cinder",
"horizon.app.core.openstack-service-api.neutron",
"horizon.app.core.openstack-service-api.security-group",
'horizon.app.core.openstack-service-api.zun',
"horizon.dashboard.container.basePath",
"horizon.framework.util.i18n.gettext",
'horizon.framework.util.q.extensions',
"horizon.framework.widgets.metadata.tree.service"
];
function workflow(cinder, neutron, basePath, gettext, treeService) {
function workflow(
cinder, neutron, securityGroup, zun, basePath, gettext,
$qExtensions, treeService
) {
var workflow = {
init: init
};
function init(action, title, submitText) {
function init(action, title, submitText, id) {
var push = Array.prototype.push;
var schema, form, model;
var imageDrivers = [
@ -495,8 +501,93 @@
// available ports
model.availablePorts = [];
// security groups
model.availableSecurityGroups = [];
model.allocatedSecurityGroups = [];
// get resources
getContainer(action, id).then(function () {
getVolumes();
getNetworks();
securityGroup.query().then(onGetSecurityGroups);
});
// get container when action equals "update"
function getContainer (action, id) {
if (action === 'create') {
return $qExtensions.booleanAsPromise(true);
} else {
return zun.getContainer(id).then(onGetContainer);
}
}
// get container for update
function onGetContainer(response) {
model.id = id;
model.name = response.data.name
? response.data.name : "";
model.image = response.data.image
? response.data.image : "";
model.image_driver = response.data.image_driver
? response.data.image_driver : "docker";
model.image_pull_policy = response.data.image_pull_policy
? response.data.image_pull_policy : "";
model.command = response.data.command
? response.data.command : "";
model.hostname = response.data.hostname
? response.data.hostname : "";
model.runtime = response.data.runtime
? response.data.runtime : "";
model.cpu = response.data.cpu
? response.data.cpu : "";
model.memory = response.data.memory
? parseInt(response.data.memory, 10) : "";
model.restart_policy = response.data.restart_policy.Name
? response.data.restart_policy.Name : "";
model.restart_policy_max_retry = response.data.restart_policy.MaximumRetryCount
? parseInt(response.data.restart_policy.MaximumRetryCount, 10) : null;
if (config.model.auto_remove) {
config.model.exit_policy = "remove";
} else {
config.model.exit_policy = config.model.restart_policy;
}
model.allocatedNetworks = getAllocatedNetworks(response.data.addresses);
model.allocatedSecurityGroups = response.data.security_groups;
model.workdir = response.data.workdir
? response.data.workdir : "";
model.environment = response.data.environment
? hashToString(response.data.environment) : "";
model.interactive = response.data.interactive
? response.data.interactive : false;
model.auto_remove = response.data.auto_remove
? response.data.auto_remove : false;
model.labels = response.data.labels
? hashToString(response.data.labels) : "";
return response;
}
function getAllocatedNetworks(addresses) {
var allocated = [];
Object.keys(addresses).forEach(function (id) {
allocated.push(id);
});
return allocated;
}
function hashToString(hash) {
var str = "";
for (var key in hash) {
if (hash.hasOwnProperty(key)) {
if (str.length > 0) {
str += ",";
}
str += key + "=" + hash[key];
}
}
return str;
}
// get available cinder volumes
getVolumes();
function getVolumes() {
return cinder.getVolumes().then(onGetVolumes);
}
@ -514,7 +605,6 @@
}
// get available neutron networks and ports
getNetworks();
function getNetworks() {
return neutron.getNetworks().then(onGetNetworks).then(getPorts);
}
@ -543,6 +633,7 @@
}
);
});
return networks;
}
function onGetPorts(ports, network) {
@ -569,6 +660,27 @@
return subnetNames;
}
// get security groups
function onGetSecurityGroups(response) {
angular.forEach(response.data.items, function (item) {
// 'default' is a special security group in neutron. It can not be
// deleted and is guaranteed to exist. It by default contains all
// of the rules needed for an instance to reach out to the network
// so the instance can provision itself.
if (item.name === 'default' && action === "create") {
model.security_groups.push(item);
}
// if network in model.allocatedSecurityGroups,
// push it to mode.security_groups for update
else if (model.allocatedSecurityGroups.includes(item.id)) {
model.security_groups.push(item);
}
});
push.apply(model.availableSecurityGroups, response.data.items);
return response;
}
var config = {
title: title,
submitText: submitText,

View File

@ -68,7 +68,8 @@
states.CREATED, states.CREATING, states.ERROR, states.RUNNING,
states.STOPPED, states.UNKNOWN, states.DELETED
],
delete_stop: [states.RUNNING]
delete_stop: [states.RUNNING],
manage_security_groups: [states.CREATED, states.RUNNING, states.STOPPED, states.PAUSED]
};
}

View File

@ -47,6 +47,7 @@
resizeContainer: resizeContainer,
attachNetwork: attachNetwork,
detachNetwork: detachNetwork,
updatePortSecurityGroup: updatePortSecurityGroup,
pullImage: pullImage,
getImages: getImages
};
@ -167,6 +168,15 @@
.error(error(msg));
}
function updatePortSecurityGroup(id, port, sgs) {
var msg = interpolate(
gettext('Unable to update security groups %(sgs)s for port %(port)s.'),
{port: port, sgs: sgs}, true);
return apiService.post(containersPath + id + '/port_update_security_groups',
{port: port, security_groups: sgs})
.error(error(msg));
}
////////////
// Images //
////////////