API v2 Dashboard

A Horizon dashboard based on the Designate v2 API.

Using some of the latest Angular code available in Mitaka Horizon and
a passthough API to avoid the need for the designate-client..

To view the new panel:
- copy the following:
  * designatedashboard/enabled/_1710_project_dns_panel_group.py
  * designatedashboard/enabled/_1721_project_ngdns_panel.py
  * designatedashboard/enabled/_1722_dns_reversedns_panel.py

into
  horizon/openstack_dashboard/local/enabled/

and restart horizon.

Change-Id: I726bffbdc9514e1dc04dcb8e888d9808c6203867
This commit is contained in:
Tyr Johanson 2016-07-12 14:50:00 -06:00 committed by Graham Hayes
parent 5627ddb4a6
commit 54e46c9275
55 changed files with 3936 additions and 0 deletions

View File

@ -0,0 +1,16 @@
# (c) Copyright <year(s)> Hewlett Packard Enterprise Development LP
#
# 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.
"""REST API for Horizon dashboard Javascript code.
"""
from . import passthrough # noqa

View File

@ -0,0 +1,114 @@
# Copyright 2016, Hewlett Packard Enterprise Development, LP
#
# 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.
"""API for the passthrough service.
"""
from django.conf import settings
from django.views import generic
import functools
import logging
import requests
from requests.exceptions import HTTPError
from horizon import exceptions
from openstack_dashboard.api import base
from openstack_dashboard.api.rest import urls
from openstack_dashboard.api.rest import utils as rest_utils
LOG = logging.getLogger(__name__)
def _passthrough_request(request_method, url,
request, data=None, params=None):
"""Makes a request to the appropriate service API with an optional payload.
Should set any necessary auth headers and SSL parameters.
"""
service = 'dns' \
''
# Set verify if a CACERT is set and SSL_NO_VERIFY isn't True
verify = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
if getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False):
verify = False
response = request_method(
_get_service_url(request, service) + url,
headers={'X-Auth-Token': request.user.token.id},
json=data,
verify=verify,
params=params
)
try:
response.raise_for_status()
except HTTPError as e:
LOG.debug(e.response.content)
for error in rest_utils.http_errors:
if (e.response.status_code == getattr(error, 'status_code', 0) and
exceptions.HorizonException in error.__bases__):
raise error
raise
return response
# Create some convenience partial functions
passthrough_get = functools.partial(_passthrough_request, requests.get)
passthrough_post = functools.partial(_passthrough_request, requests.post)
passthrough_put = functools.partial(_passthrough_request, requests.put)
passthrough_patch = functools.partial(_passthrough_request, requests.patch)
passthrough_delete = functools.partial(_passthrough_request, requests.delete)
def _get_service_url(request, service):
"""Get service's URL from keystone; allow an override in settings"""
service_url = getattr(settings, service.upper() + '_URL', None)
try:
service_url = base.url_for(request, service)
except exceptions.ServiceCatalogException:
pass
# Currently the keystone endpoint is http://host:port/
# without the version.
return service_url
@urls.register
class Passthrough(generic.View):
"""Pass-through API for executing service requests.
Horizon only adds auth and CORS proxying.
"""
url_regex = r'dns/(?P<path>.+)$'
@rest_utils.ajax()
def get(self, request, path):
return passthrough_get(path, request).json()
@rest_utils.ajax()
def post(self, request, path):
data = dict(request.DATA) if request.DATA else {}
return passthrough_post(path, request, data).json()
@rest_utils.ajax()
def put(self, request, path):
data = dict(request.DATA) if request.DATA else {}
return passthrough_put(path, request, data).json()
@rest_utils.ajax()
def patch(self, request, path):
data = dict(request.DATA) if request.DATA else {}
return passthrough_patch(path, request, data).json()
@rest_utils.ajax()
def delete(self, request, path):
return passthrough_delete(path, request).json()

View File

@ -23,6 +23,7 @@ from openstack_dashboard.api.network import tenant_floating_ip_list
from openstack_dashboard.api.nova import server_list from openstack_dashboard.api.nova import server_list
from designatedashboard import api from designatedashboard import api
from designatedashboard.api import rest # noqa
from .forms import DomainCreate # noqa from .forms import DomainCreate # noqa
from .forms import DomainUpdate # noqa from .forms import DomainUpdate # noqa

View File

@ -0,0 +1 @@
from designatedashboard.api import rest # noqa

View File

@ -0,0 +1,25 @@
# (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# 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.
from django.utils.translation import ugettext_lazy as _
import horizon
from openstack_dashboard.dashboards.project import dashboard
class ReverseDns(horizon.Panel):
name = _("Reverse DNS")
slug = 'reverse_dns'
permissions = ('openstack.services.dns',)
dashboard.Project.register(ReverseDns)

View File

@ -0,0 +1,22 @@
# (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# 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.
from django.conf.urls import url
from designatedashboard.dashboards.project.ngdns.reverse_dns import views
urlpatterns = [
url('', views.IndexView.as_view(), name='index'),
]

View File

@ -0,0 +1,19 @@
# (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# 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.
from django.views import generic
class IndexView(generic.TemplateView):
template_name = 'angular.html'

View File

@ -0,0 +1,25 @@
# (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# 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.
from django.utils.translation import ugettext_lazy as _
import horizon
from openstack_dashboard.dashboards.project import dashboard
class Zones(horizon.Panel):
name = _("Zones")
slug = 'dnszones'
permissions = ('openstack.services.dns',)
dashboard.Project.register(Zones)

View File

@ -0,0 +1,22 @@
# (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# 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.
from django.conf.urls import url
from designatedashboard.dashboards.project.ngdns.zones import views
urlpatterns = [
url('', views.IndexView.as_view(), name='index'),
]

View File

@ -0,0 +1,19 @@
# (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# 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.
from django.views import generic
class IndexView(generic.TemplateView):
template_name = 'angular.html'

View File

@ -32,3 +32,5 @@ ADD_EXCEPTIONS = {
# Python panel class of the PANEL to be added. # Python panel class of the PANEL to be added.
ADD_PANEL = ( ADD_PANEL = (
'designatedashboard.dashboards.project.dns_domains.panel.DNSDomains') 'designatedashboard.dashboards.project.dns_domains.panel.DNSDomains')
DISABLED = True

View File

@ -0,0 +1,40 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# 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.
from designatedashboard import exceptions
# The name of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'dnszones'
# The name of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'project'
# The name of the panel group the PANEL is associated with.
PANEL_GROUP = 'dns'
ADD_EXCEPTIONS = {
'recoverable': exceptions.RECOVERABLE,
'not_found': exceptions.NOT_FOUND,
'unauthorized': exceptions.UNAUTHORIZED,
}
ADD_INSTALLED_APPS = ['designatedashboard']
# Python panel class of the PANEL to be added.
ADD_PANEL = (
'designatedashboard.dashboards.project.ngdns.zones.panel.Zones')
ADD_ANGULAR_MODULES = ['designatedashboard']
ADD_SCSS_FILES = ['designatedashboard/designatedashboard.scss']
AUTO_DISCOVER_STATIC_FILES = True

View File

@ -0,0 +1,38 @@
# Copyright 2013 Hewlett-Packard Development Company, L.P.
#
# 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.
from designatedashboard import exceptions
# The name of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'reverse_dns'
# The name of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'project'
# The name of the panel group the PANEL is associated with.
PANEL_GROUP = 'dns'
ADD_EXCEPTIONS = {
'recoverable': exceptions.RECOVERABLE,
'not_found': exceptions.NOT_FOUND,
'unauthorized': exceptions.UNAUTHORIZED,
}
# Python panel class of the PANEL to be added.
ADD_PANEL = (
'designatedashboard.dashboards.project.ngdns.reverse_dns.panel.ReverseDns')
ADD_ANGULAR_MODULES = ['designatedashboard']
ADD_SCSS_FILES = ['designatedashboard/designatedashboard.scss']
AUTO_DISCOVER_STATIC_FILES = True

View File

@ -0,0 +1,78 @@
/**
* (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
*
* 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
* @ngname designatedashboard
*
* @description
* Provides the services and widgets required
* to support and display the project search panel.
*/
angular
.module('designatedashboard', [
'ngRoute',
'designatedashboard.resources'
])
.constant(
'designatedashboard.apiPassthroughUrl', '/api/dns/')
.config(config)
.run(run);
config.$inject = [
'$provide',
'$routeProvider',
'$windowProvider'
];
/**
* @name designatedashboard.basePath
* @description Base path for the project dashboard
*
* @param {function} $provide ng provide service
*
* @param {function} $routeProvider ng route service
*
* @param {function} $windowProvider NG window provider
*
* @returns {undefined}
*/
function config($provide, $routeProvider, $windowProvider) {
var path = $windowProvider.$get().STATIC_URL + 'designatedashboard/';
$provide.constant('designatedashboard.basePath', path);
$routeProvider
.when('/project/dnszones/', {
templateUrl: path + 'zones.html'
})
.when('/project/reverse_dns/', {
templateUrl: path + 'reverse_dns.html'
});
}
run.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'designatedashboard.basePath'
];
function run(registry, basePath) {
//registry.setDefaultSummaryTemplateUrl(basePath + 'table/default-drawer.html');
}
})();

View File

@ -0,0 +1,66 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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
* @ngname designatedashboard.resources.os-designate-floatingip.actions
*
* @description
* Provides all of the actions for DNS Floating IPs.
*/
angular.module('designatedashboard.resources.os-designate-floatingip.actions', [
'horizon.framework.conf',
'horizon.app.core'
])
.run(run);
run.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'designatedashboard.resources.os-designate-floatingip.resourceType',
'designatedashboard.resources.os-designate-floatingip.actions.set',
'designatedashboard.resources.os-designate-floatingip.actions.unset'
];
function run(
registry,
resourceTypeString,
setAction,
unsetAction)
{
var resourceType = registry.getResourceType(resourceTypeString);
resourceType
.itemActions
.append({
id: 'setFloatingIp',
service: setAction,
template: {
text: gettext('Set')
}
})
.append({
id: 'unsetFloatingIp',
service: unsetAction,
template: {
text: gettext('Unset')
}
});
}
})();

View File

@ -0,0 +1,170 @@
/**
*
* (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use self 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('designatedashboard.resources.os-designate-floatingip.actions')
.factory('designatedashboard.resources.os-designate-floatingip.actions.set', action);
action.$inject = [
'$q',
'designatedashboard.resources.os-designate-floatingip.api',
'designatedashboard.resources.os-designate-floatingip.resourceType',
'designatedashboard.resources.util',
'horizon.app.core.openstack-service-api.serviceCatalog',
'horizon.framework.widgets.form.ModalFormService',
'horizon.framework.widgets.toast.service',
'horizon.framework.widgets.modal-wait-spinner.service'
];
/**
* @ngDoc factory
* @name designatedashboard.resources.os-designate-floatingip.actions.set
*
* @Description
* Brings up the Set Floating IP modal.
*/
function action($q,
api,
resourceTypeName,
util,
serviceCatalog,
schemaFormModalService,
toast,
waitSpinner) {
var dnsServiceEnabled;
var title = null; // Set once perform is called
var formConfig = {
"schema": {
"type": "object",
"properties": {
"ptrdname": {
"type": "string",
"pattern": /^.+\.$/
},
"description": {
"type": "string"
},
"ttl": {
"type": "integer",
"minimum": 0,
"maximum": 2147483647
},
}
},
"form": [
{
"key": "ptrdname",
"title": gettext("Domain Name"),
"description": gettext("Domain name ending in '.'"),
"validationMessage": gettext("Domain must end with '.'"),
"placeholder": "smtp.example.com.",
"type": "text",
"required": true
},
{
"key": "description",
"type": "textarea",
"title": gettext("Description"),
"description": gettext("Details about the PTR record.")
},
{
"key": "ttl",
"title": gettext("TTL"),
"description": gettext("Time To Live in seconds."),
"type": "number"
}
]
};
var message = {
success: gettext('Domain name PTR %s was successfully set.')
};
var service = {
initScope: initScope,
allowed: allowed,
perform: perform
};
return service;
/////////////////
function initScope() {
dnsServiceEnabled = serviceCatalog.ifTypeEnabled('dns');
}
function allowed(item) {
return $q.all([
// TODO (tyr) designate currently has no floating ips policy rules
dnsServiceEnabled,
util.notPending(item)
]);
}
function perform(item) {
// Initialize the per-item title for use now and during submit
title = gettext("Set Domain Name PTR for ") + item.address;
formConfig.title = title;
// Get a form model based on the current item
formConfig.model = util.getModel(formConfig.form, item);
// Initialize default data
formConfig.model.ttl = formConfig.model.ttl || 3600;
// Remember the ID for use during submit
formConfig.model.floatingIpId = item.id;
return schemaFormModalService.open(formConfig).then(onSubmit, onCancel);
}
function onSubmit(context) {
var model = angular.copy(context.model);
var floatingIpId = formConfig.model.floatingIpId;
waitSpinner.showModalSpinner(title);
return api.set(floatingIpId, model).then(onSuccess, onFailure);
}
function onCancel() {
waitSpinner.hideModalSpinner();
}
function onSuccess(response) {
waitSpinner.hideModalSpinner();
var floatingIp = response.data;
toast.add('success', interpolate(message.success, [floatingIp.ptrdname]));
// To make the result of this action generically useful, reformat the return
// from the deleteModal into a standard form
return {
created: [],
updated: [{type: resourceTypeName, id: floatingIp.id}],
deleted: [],
failed: []
};
}
function onFailure() {
waitSpinner.hideModalSpinner();
}
}
})();

View File

@ -0,0 +1,139 @@
/**
*
* (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use self 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('designatedashboard.resources.os-designate-floatingip.actions')
.factory('designatedashboard.resources.os-designate-floatingip.actions.unset', action);
action.$inject = [
'$q',
'designatedashboard.resources.os-designate-floatingip.api',
'designatedashboard.resources.os-designate-floatingip.resourceType',
'designatedashboard.resources.util',
'horizon.app.core.openstack-service-api.serviceCatalog',
'horizon.framework.util.q.extensions',
'horizon.framework.widgets.form.ModalFormService',
'horizon.framework.widgets.toast.service',
'horizon.framework.widgets.modal-wait-spinner.service'
];
/**
* @ngDoc factory
* @name designatedashboard.resources.os-designate-floatingip.actions.unset
*
* @Description
* Brings up the Unset Floating IP modal.
*/
function action($q,
api,
resourceTypeName,
util,
serviceCatalog,
$qExtensions,
schemaFormModalService,
toast,
waitSpinner) {
var dnsServiceEnabled;
var title = null; // Set on perform
var currentFloatingIpId; // Used to remember the ID we are modifying since it isn't returned by the unset API call
// Unset it just a simple case of "set", but with ptrdname of 'null'
var formConfig = {
"schema": {
"type": "object",
"properties": {
}
},
"form": [
],
"model": {
}
};
var message = {
success: gettext('Domain name PTR successfully unset.')
};
var service = {
initScope: initScope,
allowed: allowed,
perform: perform
};
return service;
/////////////////
function initScope() {
dnsServiceEnabled = serviceCatalog.ifTypeEnabled('dns');
}
function allowed(item) {
return $q.all([
// TODO (tyr) designate currently has no floating ip policy rules
dnsServiceEnabled,
domainNameSet(item),
util.notPending(item)
]);
}
function domainNameSet(item) {
return $qExtensions.booleanAsPromise(
angular.isString(item.ptrdname)
);
}
function perform(item) {
title = gettext("Unset Domain Name PTR for ") + item.address;
// Store the zone ID so it can be used on submit
formConfig.model.floatingIpId = item.id;
formConfig.title = title;
return schemaFormModalService.open(formConfig).then(onSubmit, onCancel);
}
function onSubmit(context) {
waitSpinner.showModalSpinner(title);
currentFloatingIpId = context.model.floatingIpId;
return api.unset(currentFloatingIpId).then(onSuccess, onFailure);
}
function onCancel() {
waitSpinner.hideModalSpinner();
}
function onSuccess(response) {
waitSpinner.hideModalSpinner();
toast.add('success', message.success);
// To make the result of this action generically useful, reformat the return
// from the deleteModal into a standard form
return {
created: [],
updated: [{type: resourceTypeName, id: currentFloatingIpId}],
deleted: [],
failed: []
};
}
function onFailure() {
waitSpinner.hideModalSpinner();
}
}
})();

View File

@ -0,0 +1,121 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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('designatedashboard.resources.os-designate-floatingip')
.factory('designatedashboard.resources.os-designate-floatingip.api', apiService);
apiService.$inject = [
'designatedashboard.apiPassthroughUrl',
'horizon.framework.util.http.service',
'horizon.framework.widgets.toast.service'
];
/**
* @ngdoc service
* @param {Object} httpService
* @param {Object} toastService
* @name apiService
* @description Provides direct access to Designate Floating IP APIs.
* @returns {Object} The service
*/
function apiService(apiPassthroughUrl, httpService, toastService) {
var service = {
list: list,
get: get,
set: set,
unset: unset
};
return service;
///////////////
/**
* @name list
* @description
* Get a list of DNS floating ips.
*
* The listing result is an object with property "items." Each item is
* a floating IP PTR record.
*
* @param {Object} params
* Query parameters. Optional.
*
* @returns {Object} The result of the API call
*/
function list(params) {
var config = params ? {'params': params} : {};
return httpService.get(apiPassthroughUrl + 'v2/reverse/floatingips', config)
.error(function () {
toastService.add('error', gettext('Unable to retrieve the floating ip PTRs.'));
});
}
function get(id, params) {
var config = params ? {'params': params} : {};
return httpService.get(apiPassthroughUrl + 'v2/reverse/floatingips/' + id, config)
.error(function () {
toastService.add('error', gettext('Unable to get the floating ip PTR ' + id));
});
}
/**
* @name set
* @description
* Set a floating ip PTR record
*
* @param {string} floatingIpID - ID of PTR record to unset
* @param {Object} data
* Specifies the PTR information to set
*
* @returns {Object} The updated DNS floating IP object
*/
function set(floatingIpID, data) {
// The update API will not accept extra data. Restrict the input to only the allowed
// fields
var apiData = {
ptrdname: data.ptrdname,
description: data.description,
ttl: data.ttl
};
return httpService.patch(apiPassthroughUrl + 'v2/reverse/floatingips/' + floatingIpID, apiData)
.error(function () {
toastService.add('error', gettext('Unable to set the floating IP PTR record.'));
})
}
/**
* @name unset
* @description
* Unset a floating ip PTR record
*
* @param {string} floatingIpID - ID of PTR record to unset
*
* @returns {Object} The updated DNS floating IP object
*/
function unset(floatingIpID) {
// Unset is just a special case of 'set'
return set(floatingIpID, {
ptrdname: null,
description: null,
ttl: null
})
}
}
}());

View File

@ -0,0 +1,66 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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
* @ngname designatedashboard.resources.os-designate-floatingip.details
*
* @description
* Provides details features for floating IPs.
*/
angular.module('designatedashboard.resources.os-designate-floatingip.details',
['horizon.framework.conf', 'horizon.app.core'])
.run(run);
run.$inject = [
'designatedashboard.resources.os-designate-floatingip.resourceType',
'designatedashboard.resources.os-designate-floatingip.api',
'designatedashboard.resources.os-designate-floatingip.basePath',
'horizon.framework.conf.resource-type-registry.service'
];
function run(
resourceTypeName,
api,
basePath,
registry
) {
var resourceType = registry.getResourceType(resourceTypeName);
resourceType
.setLoadFunction(loadFunction)
.setSummaryTemplateUrl(basePath + 'details/drawer.html')
.setItemNameFunction(itemNameFunction);
resourceType.detailsViews
.prepend({
id: 'floatingIpDetailsOverview',
name: gettext('Overview'),
template: basePath + 'details/overview.html'
}, 0);
function loadFunction(identifier) {
return api.get(identifier);
}
function itemNameFunction(floatingIp) {
return floatingIp.address;
}
}
})();

View File

@ -0,0 +1,10 @@
<hz-resource-property-list
ng-if="item"
resource-type-name="OS::Designate::FloatingIp"
item="item"
cls="dl-horizontal"
property-groups="[
['description'],
['id']
]">
</hz-resource-property-list>

View File

@ -0,0 +1,46 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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('designatedashboard.resources.os-designate-floatingip.details')
.controller('designatedashboard.resources.os-designate-floatingip.details.overviewController', controller);
controller.$inject = [
'designatedashboard.resources.os-designate-floatingip.resourceType',
'horizon.framework.conf.resource-type-registry.service',
'$scope'
];
function controller(
resourceTypeCode,
registry,
$scope
) {
var ctrl = this;
ctrl.item;
ctrl.resourceType = registry.getResourceType(resourceTypeCode);
$scope.context.loadPromise.then(onGetResponse);
function onGetResponse(response) {
ctrl.item = response.data;
}
}
})();

View File

@ -0,0 +1,11 @@
<div ng-controller="designatedashboard.resources.os-designate-floatingip.details.overviewController as ctrl">
<hz-resource-property-list
ng-if="ctrl.item"
resource-type-name="OS::Designate::FloatingIp"
item="ctrl.item"
cls="dl-horizontal"
property-groups="[
['ptrdname', 'description', 'id', 'ttl', 'status', 'action']
]">
</hz-resource-property-list>
</div>

View File

@ -0,0 +1,154 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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
* @ngname designatedashboard.resources.os-designate-floatingip
*
* @description
* Provides all of the services and widgets required
* to support and display DNS (designate) floating ip related content.
*/
angular
.module('designatedashboard.resources.os-designate-floatingip', [
'ngRoute',
'designatedashboard.resources.os-designate-floatingip.actions',
'designatedashboard.resources.os-designate-floatingip.details'
])
.constant(
'designatedashboard.resources.os-designate-floatingip.resourceType',
'OS::Designate::FloatingIp')
.config(config)
.run(run);
config.$inject = [ '$provide', '$windowProvider' ];
function config($provide, $windowProvider) {
var path = $windowProvider.$get().STATIC_URL + 'designatedashboard/resources/os-designate-floatingip/';
$provide.constant('designatedashboard.resources.os-designate-floatingip.basePath', path);
}
run.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'designatedashboard.resources.os-designate-floatingip.api',
'designatedashboard.resources.os-designate-floatingip.resourceType',
'designatedashboard.resources.util'
];
function run(
registry,
api,
resourceTypeString,
util)
{
var resourceType = registry.getResourceType(resourceTypeString);
resourceType
.setNames(gettext('Floating IP'), gettext('Floating IPs'))
.setListFunction(listFloatingIps)
.setProperty('id', {
label: gettext('ID')
})
.setProperty('ptrdname', {
label: gettext('PTR Domain Name'),
filters: ['noName']
})
.setProperty('description', {
label: gettext('Description'),
filters: ['noName']
})
.setProperty('ttl', {
label: gettext('Time To Live'),
filters: ['noValue']
})
.setProperty('address', {
label: gettext('Address'),
filters: ['noName']
})
.setProperty('status', {
label: gettext('Status'),
filters: ['lowercase', 'noName'],
values: util.statusMap()
})
.setProperty('action', {
label: gettext('Action'),
filters: ['lowercase', 'noName'],
values: util.actionMap()
});
resourceType
.tableColumns
.append({
id: 'address',
priority: 1,
sortDefault: true,
template: '<a ng-href="{$ \'project/ngdetails/OS::Designate::FloatingIp/\' + item.id $}">{$ item.address $}</a>'
})
.append({
id: 'ptrdname',
filters: ['noValue'],
priority: 1,
})
.append({
id: 'status',
filters: ['lowercase'],
values: util.statusMap(),
priority: 2
});
resourceType
.filterFacets
.append({
label: gettext('Address'),
name: 'address',
isServer: false,
singleton: true,
persistent: false
})
.append({
label: gettext('PTR Domain Name'),
name: 'ptrdname',
isServer: false,
singleton: true,
persistent: false
})
.append({
label: gettext('Status'),
name: 'status',
isServer: false,
singleton: true,
persistent: false,
options: [
{label: gettext('Active'), key: 'active'},
{label: gettext('Pending'), key: 'pending'}
]
});
function listFloatingIps() {
return api.list().then(function onList(response) {
// listFunctions are expected to return data in "items"
response.data.items = response.data.floatingips;
util.addTimestampIds(response.data.items);
return response;
});
}
}
})();

View File

@ -0,0 +1,79 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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
* @ngname designatedashboard.resources.os-designate-recordset.actions
*
* @description
* Provides all of the actions for DNS Recordsets.
*/
angular.module('designatedashboard.resources.os-designate-recordset.actions', [
'horizon.framework.conf',
'horizon.app.core'
])
.run(run);
run.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'designatedashboard.resources.os-designate-recordset.resourceType',
'designatedashboard.resources.os-designate-recordset.actions.create',
'designatedashboard.resources.os-designate-recordset.actions.delete',
'designatedashboard.resources.os-designate-recordset.actions.update'
];
function run(registry,
resourceTypeString,
createAction,
deleteAction,
updateAction) {
var resourceType = registry.getResourceType(resourceTypeString);
resourceType
.itemActions
.append({
id: 'updateRecordset',
service: updateAction,
template: {
text: gettext('Update')
}
})
.append({
id: 'deleteRecordset',
service: deleteAction,
template: {
text: gettext('Delete'),
type: 'delete'
}
});
// Append a record set view to the zones actions
var zoneResourceType = registry.getResourceType("OS::Designate::Zone");
zoneResourceType
.itemActions
.append({
id: 'createRecordset',
service: createAction,
template: {
text: gettext('Create Record Set')
}
});
}
})();

View File

@ -0,0 +1,165 @@
/**
*
* (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use self 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('designatedashboard.resources.os-designate-recordset.actions')
.factory('designatedashboard.resources.os-designate-recordset.actions.common-forms', service);
service.$inject = [
'designatedashboard.resources.os-designate-recordset.editableTypes',
'designatedashboard.resources.os-designate-recordset.typeMap'
];
/**
* Service to return a schema form config for action forms. Especially useful for forms
* like create and update that differ only in the readonly state of certain form fields.
*
* @returns {object} A schema form config
*/
function service(editableTypes, typeMap) {
var service = {
getCreateFormConfig: getCreateFormConfig,
getUpdateFormConfig: getUpdateFormConfig
};
return service;
/////////////////
/**
* Returns the create form config
* @returns {{schema, form, model}|*}
*/
function getCreateFormConfig() {
return getCreateUpdateFormConfig(false);
}
/**
* Return the update form config
* @returns {{schema, form, model}|*}
*/
function getUpdateFormConfig() {
return getCreateUpdateFormConfig(true);
}
/**
* Return the create/update form. The two forms are identical except for
* during update, some fields are read-only.
*
* @param readonly - sets readonly value on form fields that change between update and create
* @returns {object} a schema form config, including default model
*/
function getCreateUpdateFormConfig(readonly) {
return {
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"pattern": /^.+\.$/
},
"description": {
"type": "string"
},
"type": {
"type": "string",
"enum": editableTypes
},
"ttl": {
"type": "integer",
"minimum": 1,
"maximum": 2147483647
},
"records": {
"type": "array",
"items": {
"type": "object",
"properties": {
"record": {
"type": "string"
}
}
},
"minItems": 1,
"uniqueItems": true
}
}
},
"form": [
{
"key": "type",
"readonly": readonly,
"title": gettext("Type"),
"description": gettext("Select the type of record set"),
"type": "select",
"titleMap": editableTypes.map(function toTitleMap(type) {
return {
"value": type,
"name": typeMap[type]
}
}),
"required": true
},
{
"key": "name",
"readonly": readonly,
"type": "text",
"title": gettext("Name"),
"description": gettext("DNS name for the record set, ending in '.'"),
"validationMessage": gettext("DNS name must end with '.'"),
"placeholder": "www.example.com.",
"required": true
},
{
"key": "description",
"type": "textarea",
"title": gettext("Description"),
"description": gettext("Details about the zone.")
},
{
"key": "ttl",
"title": gettext("TTL"),
"description": gettext("Time To Live in seconds."),
"type": "number",
"required": true
},
{
"key": "records",
"title": gettext("Records"),
"type": "array",
"description": gettext("Records for the record set."),
"add": gettext("Add Record"),
"items": [
{
"key": "records[].record",
"title": gettext("Record")
}
],
"required": true
}
],
"model": {
"type": "A",
"ttl": 3600
}
};
}
}
})();

View File

@ -0,0 +1,132 @@
/**
*
* (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use self 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('designatedashboard.resources.os-designate-recordset.actions')
.factory('designatedashboard.resources.os-designate-recordset.actions.create', action);
action.$inject = [
'$q',
'designatedashboard.resources.os-designate-recordset.actions.common-forms',
'designatedashboard.resources.os-designate-recordset.api',
'designatedashboard.resources.os-designate-recordset.resourceType',
'horizon.app.core.openstack-service-api.policy',
'horizon.app.core.openstack-service-api.serviceCatalog',
'horizon.framework.conf.resource-type-registry.service',
'horizon.framework.widgets.form.ModalFormService',
'horizon.framework.widgets.toast.service',
'horizon.framework.widgets.modal-wait-spinner.service'
];
/**
* @ngDoc factory
* @name designatedashboard.resources.os-designate-recordset.actions.create
*
* @Description
* Brings up the Create Record Set modal.
*/
function action($q,
forms,
api,
resourceTypeName,
policy,
serviceCatalog,
registry,
schemaFormModalService,
toast,
waitSpinner) {
var createRecordSetPolicy, dnsServiceEnabled;
var title = gettext("Create Record Set");
var message = {
success: gettext('Record Set %s was successfully created.')
};
var service = {
initScope: initScope,
allowed: allowed,
perform: perform
};
return service;
/////////////////
function initScope() {
createRecordSetPolicy = policy.ifAllowed({rules: [['dns', 'create_recordset']]});
dnsServiceEnabled = serviceCatalog.ifTypeEnabled('dns');
}
function allowed(item) {
return $q.all([
createRecordSetPolicy,
dnsServiceEnabled
]);
}
function perform(item) {
var formConfig = forms.getCreateFormConfig();
// Store the zone ID so it can be used on submit
formConfig.model.zoneId = item.id;
formConfig.title = title;
return schemaFormModalService.open(formConfig).then(onSubmit, onCancel);
}
function onSubmit(context) {
var model = angular.copy(context.model);
var zoneId = model.zoneId;
delete model.zoneId;
// schema form doesn't appear to support populating arrays directly
// Map the records objects to simple array of records
var records = context.model.records.map(function (item) {
return item.record;
});
model.records = records;
waitSpinner.showModalSpinner(gettext('Creating Record Set'));
return api.create(zoneId, model).then(onSuccess, onFailure);
}
function onCancel() {
waitSpinner.hideModalSpinner();
}
function onSuccess(response) {
waitSpinner.hideModalSpinner();
var zone = response.data;
toast.add('success', interpolate(message.success, [zone.name]));
// To make the result of this action generically useful, reformat the return
// from the deleteModal into a standard form
return {
created: [{type: resourceTypeName, id: zone.id}],
updated: [],
deleted: [],
failed: []
};
}
function onFailure() {
waitSpinner.hideModalSpinner();
}
}
})();

View File

@ -0,0 +1,182 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use self 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('designatedashboard.resources.os-designate-recordset.actions')
.factory('designatedashboard.resources.os-designate-recordset.actions.delete', action);
action.$inject = [
'$q',
'designatedashboard.resources.os-designate-recordset.api',
'designatedashboard.resources.os-designate-recordset.editableTypes',
'designatedashboard.resources.os-designate-recordset.resourceType',
'horizon.app.core.openstack-service-api.policy',
'horizon.framework.util.actions.action-result.service',
'horizon.framework.util.i18n.gettext',
'horizon.framework.util.q.extensions',
'horizon.framework.widgets.modal.deleteModalService',
'horizon.framework.widgets.toast.service'
];
/*
* @ngdoc factory
* @name designatedashboard.resources.os-designate-recordset.actions.delete
*
* @Description
* Brings up the delete recordset confirmation modal dialog.
* On submit, delete given recordset.
* On cancel, do nothing.
*/
function action(
$q,
recordsetApi,
editableTypes,
resourceType,
policy,
actionResultService,
gettext,
$qExtensions,
deleteModal,
toast
) {
var scope, context, deletePromise;
var notAllowedMessage = gettext("You are not allowed to delete record sets: %s");
var allowedRecordsets = [];
var service = {
initScope: initScope,
allowed: allowed,
perform: perform
};
return service;
//////////////
function initScope(newScope) {
scope = newScope;
context = { };
deletePromise = policy.ifAllowed({rules: [['dns', 'delete_recordset']]});
}
function perform(items) {
var recordsets = angular.isArray(items) ? items : [items];
context.labels = labelize(recordsets.length);
context.deleteEntity = deleteRecordSet;
return $qExtensions.allSettled(recordsets.map(checkPermission)).then(afterCheck);
}
function allowed(recordset) {
// only row actions pass in recordset
// otherwise, assume it is a batch action
if (recordset) {
return $q.all([
deletePromise,
editableRecordType(recordset)
]);
} else {
return policy.ifAllowed({ rules: [['dns', 'delete_recordset']] });
}
}
function checkPermission(recordset) {
return {promise: allowed(recordset), context: recordset};
}
function afterCheck(result) {
var outcome = $q.reject(); // Reject the promise by default
if (result.fail.length > 0) {
toast.add('error', getMessage(notAllowedMessage, result.fail));
outcome = $q.reject(result.fail);
}
if (result.pass.length > 0) {
// Remember the record sets we are allowed to delete so that on delete modal submit
// we can map the recordset ID back to the full recordset. Then we can fetch the
// corresponding zone ID
allowedRecordsets = result.pass.map(getEntity)
outcome = deleteModal.open(scope, allowedRecordsets, context).then(createResult);
}
return outcome;
}
function createResult(deleteModalResult) {
// To make the result of this action generically useful, reformat the return
// from the deleteModal into a standard form
var actionResult = actionResultService.getActionResult();
deleteModalResult.pass.forEach(function markDeleted(item) {
actionResult.deleted(resourceType, getEntity(item).id);
});
deleteModalResult.fail.forEach(function markFailed(item) {
actionResult.failed(resourceType, getEntity(item).id);
});
return actionResult.result;
}
function labelize(count) {
return {
title: ngettext(
'Confirm Delete Record Set',
'Confirm Delete Record Sets', count),
message: ngettext(
'You have selected "%s". Deleted record set is not recoverable.',
'You have selected "%s". Deleted record sets are not recoverable.', count),
submit: ngettext(
'Delete Record Set',
'Delete Record Sets', count),
success: ngettext(
'Deleted Record Set: %s.',
'Deleted Record Sets: %s.', count),
error: ngettext(
'Unable to delete Record Set: %s.',
'Unable to delete Record Sets: %s.', count)
};
}
function editableRecordType(recordset) {
return $qExtensions.booleanAsPromise(
editableTypes.indexOf(recordset.type) > -1
);
}
function deleteRecordSet(recordSetId) {
var recordSet = allowedRecordsets.find(function(element) {
return element.id === recordSetId;
})
return recordsetApi.deleteRecordSet(recordSet.zone_id, recordSet.id);
}
function getMessage(message, entities) {
return interpolate(message, [entities.map(getName).join(", ")]);
}
function getName(result) {
return getEntity(result).name;
}
function getEntity(result) {
return result.context;
}
}
})();

View File

@ -0,0 +1,159 @@
/**
*
* (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use self 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('designatedashboard.resources.os-designate-recordset.actions')
.factory('designatedashboard.resources.os-designate-recordset.actions.update', action);
action.$inject = [
'$q',
'designatedashboard.resources.util',
'designatedashboard.resources.os-designate-recordset.actions.common-forms',
'designatedashboard.resources.os-designate-recordset.api',
'designatedashboard.resources.os-designate-recordset.editableTypes',
'designatedashboard.resources.os-designate-recordset.resourceType',
'horizon.app.core.openstack-service-api.policy',
'horizon.app.core.openstack-service-api.serviceCatalog',
'horizon.framework.util.q.extensions',
'horizon.framework.widgets.form.ModalFormService',
'horizon.framework.widgets.toast.service',
'horizon.framework.widgets.modal-wait-spinner.service'
];
/**
* @ngDoc factory
* @name designatedashboard.resources.os-designate-recordset.actions.update
*
* @Description
* Brings up the Update modal.
*/
function action($q,
util,
forms,
api,
editableTypes,
resourceTypeName,
policy,
serviceCatalog,
$qExtensions,
schemaFormModalService,
toast,
waitSpinner) {
var updateRecordSetPolicy, dnsServiceEnabled;
var title = gettext("Update Record Set");
var message = {
success: gettext('Record Set %s was successfully updated.')
};
var service = {
initScope: initScope,
allowed: allowed,
perform: perform
};
return service;
/////////////////
function initScope() {
updateRecordSetPolicy = policy.ifAllowed({rules: [['dns', 'update_recordset']]});
dnsServiceEnabled = serviceCatalog.ifTypeEnabled('dns');
}
function allowed(recordset) {
// only supports row action (exactly 1 recordset)
if (recordset) {
return $q.all([
updateRecordSetPolicy,
util.notDeleted(recordset),
util.notPending(recordset),
editableRecordType(recordset)
]);
} else {
return false;
}
}
function editableRecordType(recordset) {
return $qExtensions.booleanAsPromise(
editableTypes.indexOf(recordset.type) > -1
);
}
function perform(item) {
var formConfig = forms.getUpdateFormConfig();
formConfig.title = title;
formConfig.model = util.getModel(formConfig.form, item);
// Append the id and zoneId so it can be used on submit
formConfig.model.id = item.id;
formConfig.model.zoneId = item.zone_id;
// schema form doesn't appear to support populating the records array directly
// Map the records objects to record objects
if (item.hasOwnProperty("records")) {
var records = item.records.map(function (item) {
return {"record": item}
});
formConfig.model.records = records;
}
return schemaFormModalService.open(formConfig).then(onSubmit, onCancel);
}
function onSubmit(context) {
var model = angular.copy(context.model);
// schema form doesn't appear to support populating the records array directly
// Map the records objects to simple array of records
if (context.model.hasOwnProperty("records")) {
var records = context.model.records.map(function (item) {
return item.record;
});
model.records = records;
}
waitSpinner.showModalSpinner(gettext('Updating Record Set'));
return api.update(model.zoneId, model.id, model).then(onSuccess, onFailure);
}
function onCancel() {
waitSpinner.hideModalSpinner();
}
function onSuccess(response) {
waitSpinner.hideModalSpinner();
var recordset = response.data;
toast.add('success', interpolate(message.success, [recordset.name]));
// To make the result of this action generically useful, reformat the return
// from the deleteModal into a standard form
return {
created: [],
updated: [{type: resourceTypeName, id: recordset.id}],
deleted: [],
failed: []
};
}
function onFailure() {
waitSpinner.hideModalSpinner();
}
}
})();

View File

@ -0,0 +1,136 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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('designatedashboard.resources.os-designate-recordset')
.factory('designatedashboard.resources.os-designate-recordset.api', apiService);
apiService.$inject = [
'$q',
'designatedashboard.apiPassthroughUrl',
'horizon.framework.util.http.service',
'horizon.framework.widgets.toast.service'
];
/**
* @ngdoc service
* @param {Object} httpService
* @param {Object} toastService
* @name apiService
* @description Provides direct access to Designate Record Set APIs.
* @returns {Object} The service
*/
function apiService($q, apiPassthroughUrl, httpService, toastService) {
var service = {
get: get,
list: list,
deleteRecordSet: deleteRecordSet,
create: create,
update: update
};
return service;
///////////////
/**
* @name list
* @description
* Get a list of record sets.
*
* The listing result is an object with property "items." Each item is
* a record set.
*
* @param {Object} params
* Query parameters. Optional.
*
* @returns {Object} The result of the API call
*/
function list(zoneId, params) {
return httpService.get(apiPassthroughUrl + 'v2/zones/' + zoneId + '/recordsets/', params)
.error(function () {
toastService.add('error', gettext('Unable to retrieve the record sets.'));
});
}
/**
* @name get
* @description
* Get a single record set by ID.
*
* @param {string} zoneId
* Specifies the id of the zone containing the record set to request.
*
* @param {string} recordSetId
* Specifies the id of the record set to request.
*
* @returns {Object} The result of the API call
*/
function get(zoneId, recordSetId) {
// Unfortunately routed-details-view is not happy when load fails, which is
// common when then delete action removes a record set. Mask this failure by
// always returning a successful promise instead of terminating the $http promise
// in the .error handler.
return httpService.get(apiPassthroughUrl + 'v2/zones/' + zoneId + '/recordsets/' + recordSetId + '/')
.then(undefined, function onError() {
toastService.add('error', gettext('Unable to retrieve the record set.'));
return $q.when({});
});
}
/**
* @name delete
* @description
* Delete a single record set by ID
* @param {string} zoneId
* The id of the zone containing the recordset
*
* @param {string} recordSetId
* The id of the recordset within the zone
*
* @returns {*}
*/
function deleteRecordSet(zoneId, recordSetId) {
return httpService.delete(apiPassthroughUrl + 'v2/zones/' + zoneId + '/recordsets/' + recordSetId + '/')
.error(function () {
toastService.add('error', gettext('Unable to delete the record set.'));
});
}
function create(zoneId, data) {
return httpService.post(apiPassthroughUrl + 'v2/zones/' + zoneId + '/recordsets/', data)
.error(function () {
toastService.add('error', gettext('Unable to create the record set.'));
});
}
function update(zoneId, recordSetId, data) {
// The update API will not accept extra data. Restrict the input to only the allowed
// fields
var apiData = {
ttl: data.ttl,
description: data.description,
records: data.records
};
return httpService.put(apiPassthroughUrl + 'v2/zones/' + zoneId + '/recordsets/' + recordSetId, apiData)
.error(function () {
toastService.add('error', gettext('Unable to update the record set.'));
});
}
}
}());

View File

@ -0,0 +1,109 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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
* @ngname designatedashboard.resources.os-designate-recordset.details
*
* @description
* Provides details features for record sets.
*/
angular.module('designatedashboard.resources.os-designate-recordset.details',
['horizon.framework.conf', 'horizon.app.core'])
.run(run);
run.$inject = [
'designatedashboard.resources.os-designate-recordset.resourceType',
'designatedashboard.resources.os-designate-recordset.api',
'designatedashboard.resources.os-designate-recordset.basePath',
'horizon.framework.conf.resource-type-registry.service',
];
function run(
recordSetResourceType,
recordSetApi,
basePath,
registry
) {
var resourceType = registry.getResourceType(recordSetResourceType);
resourceType
.setLoadFunction(loadFunction)
.setPathGenerator(pathGenerator)
.setPathParser(pathParser)
.setSummaryTemplateUrl(basePath + 'details/drawer.html');
/**
*
* @param identifier
* The object returned by the pathParser containing the zone ID and record set ID to load
*/
function loadFunction(identifier) {
return recordSetApi.get(identifier.zoneId, identifier.recordSetId);
}
/**
* Because a record set is contained by a zone, we implement a custom
* pathGenerator to encode the zone ID and record set ID for the generic
* details panel.
*
* @param item
* A record set
*
* @returns {string} In format "<zone_id>/<recordset_id>"
*/
function pathGenerator(item) {
return item.zone_id + '/' + item.id;
}
/**
* Given a path, extract the zone and record set ids
*
* @param path
* created by pathGenerator
*
* @returns {{zoneId: *, recordSetId: *}}
* The identifier to pass to the load function needed to uniquely identify
* a record set.
*/
function pathParser(path) {
var split = path.split('/');
return {
zoneId: split[0],
recordSetId: split[1]
}
}
resourceType.detailsViews
.prepend({
id: 'recordsetDetailsOverview',
name: gettext('Overview'),
template: basePath + 'details/overview.html',
}, 0);
// Append a record set view to the zones resource view
var zoneResourceType = registry.getResourceType("OS::Designate::Zone");
zoneResourceType.detailsViews
.append({
id: 'zoneRecordSets',
name: gettext('Record Sets'),
template: basePath + 'details/zone-recordsets.html',
});
}
})();

View File

@ -0,0 +1,10 @@
<hz-resource-property-list
ng-if="item"
resource-type-name="OS::Designate::RecordSet"
item="item"
cls="dl-horizontal"
property-groups="[
['records', 'notes', 'description'],
['id']
]">
</hz-resource-property-list>

View File

@ -0,0 +1,46 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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('designatedashboard.resources.os-designate-recordset')
.controller('designatedashboard.resources.os-designate-recordset.detailController', controller);
controller.$inject = [
'designatedashboard.resources.os-designate-recordset.resourceType',
'horizon.framework.conf.resource-type-registry.service',
'$scope'
];
function controller(
resourceTypeCode,
registry,
$scope
) {
var ctrl = this;
ctrl.item = {};
ctrl.resourceType = registry.getResourceType(resourceTypeCode);
$scope.context.loadPromise.then(onGetResponse);
function onGetResponse(response) {
ctrl.item = response.data;
}
}
})();

View File

@ -0,0 +1,45 @@
<div ng-controller="designatedashboard.resources.os-designate-recordset.detailController as ctrl">
<div class="row">
<div class="col-md-6 detail">
<h3 translate>Details</h3>
<hr>
<hz-resource-property-list
ng-if="ctrl.item"
resource-type-name="OS::Designate::RecordSet"
item="ctrl.item"
cls="dl-horizontal"
property-groups="[
['id', 'type', 'name', 'records', 'notes', 'description', 'ttl', 'version', 'action', 'status']
]">
</hz-resource-property-list>
</div>
<div class="col-md-6 detail">
<h3 translate>Associations</h3>
<hr>
<hz-resource-property-list
ng-if="ctrl.item"
resource-type-name="OS::Designate::RecordSet"
item="ctrl.item"
cls="dl-horizontal"
property-groups="[
['zone_id', 'zone_name', 'project_id']
]">
</hz-resource-property-list>
</div>
</div>
<div class="row">
<div class="col-md-6 detail">
<h3 translate>Modification Times</h3>
<hr>
<hz-resource-property-list
ng-if="ctrl.item"
resource-type-name="OS::Designate::RecordSet"
item="ctrl.item"
cls="dl-horizontal"
property-groups="[
['created_at', 'updated_at']
]">
</hz-resource-property-list>
</div>
</div>
</div>

View File

@ -0,0 +1,39 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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('designatedashboard.resources.os-designate-recordset')
.controller('designatedashboard.resources.os-designate-recordset.zoneRecordSetsController', controller);
controller.$inject = [
'$scope',
'designatedashboard.resources.os-designate-recordset.resourceType'
];
function controller(
$scope,
resourceTypeName
) {
var ctrl = this;
ctrl.item = {};
ctrl.resourceTypeName = resourceTypeName;
ctrl.extraListParams = { zoneId: $scope.context.identifier };
}
})();

View File

@ -0,0 +1,8 @@
<div
ng-controller="designatedashboard.resources.os-designate-recordset.zoneRecordSetsController as ctrl">
<hz-resource-table
resource-type-name="{$ ctrl.resourceTypeName $}"
list-function-extra-params="ctrl.extraListParams"
track-by="_timestampId">
</hz-resource-table>
</div>

View File

@ -0,0 +1,238 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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
* @ngname designatedashboard.resources.os-designate-recordset
*
* @description
* Provides all of the services and widgets required
* to support and display DNS (designate) record set related content.
*/
angular
.module('designatedashboard.resources.os-designate-recordset', [
'ngRoute',
'designatedashboard.resources.os-designate-recordset.actions',
'designatedashboard.resources.os-designate-recordset.details'
])
.constant(
'designatedashboard.resources.os-designate-recordset.resourceType',
'OS::Designate::RecordSet')
.constant(
'designatedashboard.resources.os-designate-recordset.typeMap',
{
'A': gettext('A - Address record'),
'AAAA': gettext('AAAA - IPv6 address record'),
'CNAME': gettext('CNAME - Canonical name record'),
'MX': gettext('MX - Mail exchange record'),
'PTR': gettext('PTR - Pointer record'),
'SPF': gettext('SPR - Sender Policy Framework'),
'SRV': gettext('SRV - Service locator'),
'SSHFP': gettext('SSHFP - SSH Public Key Fingerprint'),
'TXT': gettext('TXT - Text record'),
'SOA': gettext('SOA - Start of authority record'),
'NS': gettext('NS - Name server')
})
.constant(
'designatedashboard.resources.os-designate-recordset.editableTypes',
[
"A",
"AAAA",
"CNAME",
"MX",
"PTR",
"SPF",
"SRV",
"SSHFP",
"TXT"
])
.config(config)
.run(run);
config.$inject = ['$provide', '$windowProvider'];
function config($provide, $windowProvider) {
var path = $windowProvider.$get().STATIC_URL + 'designatedashboard/resources/os-designate-recordset/';
$provide.constant('designatedashboard.resources.os-designate-recordset.basePath', path);
}
run.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'designatedashboard.resources.os-designate-recordset.api',
'designatedashboard.resources.os-designate-recordset.resourceType',
'designatedashboard.resources.os-designate-recordset.typeMap',
'designatedashboard.resources.util'
];
function run(registry,
recordSetApi,
resourceTypeString,
typeMap,
util) {
var resourceType = registry.getResourceType(resourceTypeString);
resourceType
.setNames(gettext('DNS Record Set'), gettext('DNS Record Sets'))
.setListFunction(list)
.setProperty('id', {
label: gettext('ID')
})
.setProperty('zone_id', {
label: gettext('Zone ID'),
filters: ['noValue']
})
.setProperty('zone_name', {
label: gettext('Zone Name'),
filters: ['noName']
})
.setProperty('project_id', {
label: gettext('Project ID'),
filters: ['noValue']
})
.setProperty('name', {
label: gettext('Name'),
filters: ['noName']
})
.setProperty('description', {
label: gettext('Description'),
filters: ['noName']
})
.setProperty('type', {
label: gettext('Type'),
filters: ['uppercase'],
values: typeMap
})
.setProperty('ttl', {
label: gettext('Time To Live'),
filters: ['noValue']
})
.setProperty('records', {
label: gettext('Records'),
filters: ['noValue']
})
.setProperty('notes', {
label: gettext('Notes'),
filters: ['noName']
})
.setProperty('status', {
label: gettext('Status'),
filters: ['lowercase', 'noName'],
values: util.statusMap()
})
.setProperty('action', {
label: gettext('Action'),
filters: ['lowercase', 'noName'],
values: util.actionMap()
})
.setProperty('version', {
label: gettext('Version'),
filters: ['noValue']
})
.setProperty('created_at', {
label: gettext('Created At'),
filters: ['noValue']
})
.setProperty('updated_at', {
label: gettext('Updated At'),
filters: ['noValue']
});
resourceType
.tableColumns
.append({
id: 'name',
priority: 1,
sortDefault: true,
filters: ['noName'],
// For link format, see pathGenerator in details.module.js
template: '<a ng-href="{$ \'project/ngdetails/OS::Designate::RecordSet/\' + item.zone_id + \'/\' + item.id $}">{$ item.name $}</a>'
})
.append({
id: 'type',
priority: 2,
filters: ['uppercase'],
values: typeMap
})
.append({
id: 'records',
priority: 2,
filters: ['noValue']
})
.append({
id: 'status',
filters: ['lowercase'],
values: util.statusMap(),
priority: 2
});
resourceType
.filterFacets
.append({
label: gettext('Type'),
name: 'type',
isServer: false,
singleton: true,
persistent: false,
options: Object.keys(typeMap).map(function toOptionLabel(key) {
return {
label: typeMap[key],
key: key
}
})
})
.append({
label: gettext('Name'),
name: 'name',
isServer: false,
singleton: true,
persistent: false
})
.append({
label: gettext('Status'),
name: 'status',
isServer: false,
singleton: true,
persistent: false,
options: [
{label: gettext('Active'), key: 'active'},
{label: gettext('Pending'), key: 'pending'}
]
});
/**
* list all recordsets within a zone. Requires "zoneId" in the params. All other
* params will be passed unmodified as URL params to the API.
*
* @param params
* zoneId (required) list recordsets within the zone
*
* @returns {*|Object}
*/
function list(params) {
return recordSetApi.list(params.zoneId, params).then(function onList(response) {
// listFunctions are expected to return data in "items"
response.data.items = response.data.recordsets;
util.addTimestampIds(response.data.items);
return response;
});
}
}
})();

View File

@ -0,0 +1,76 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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
* @ngname designatedashboard.resources.os-designate-zone.actions
*
* @description
* Provides all of the actions for DNS Zones.
*/
angular.module('designatedashboard.resources.os-designate-zone.actions', [
'horizon.framework.conf',
'horizon.app.core'
])
.run(run);
run.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'designatedashboard.resources.os-designate-zone.resourceType',
'designatedashboard.resources.os-designate-zone.actions.create',
'designatedashboard.resources.os-designate-zone.actions.delete',
'designatedashboard.resources.os-designate-zone.actions.update'
];
function run(registry,
resourceTypeString,
createAction,
deleteAction,
updateAction) {
var resourceType = registry.getResourceType(resourceTypeString);
resourceType
.globalActions
.append({
id: 'create',
service: createAction,
template: {
text: gettext('Create Zone')
}
});
resourceType
.itemActions
.append({
id: 'update',
service: updateAction,
template: {
text: gettext('Update')
}
})
.append({
id: 'delete',
service: deleteAction,
template: {
text: gettext('Delete'),
type: 'delete'
}
});
}
})();

View File

@ -0,0 +1,184 @@
/**
*
* (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use self 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('designatedashboard.resources.os-designate-zone.actions')
.factory('designatedashboard.resources.os-designate-zone.actions.common-forms', service);
service.$inject = [
];
/**
* Service to return a schema form config for action forms. Especially useful for forms
* like create and update that differ only in the readonly state of certain form fields.
*
* @returns {object} A schema form config
*/
function service() {
var service = {
getCreateFormConfig: getCreateFormConfig,
getUpdateFormConfig: getUpdateFormConfig
};
return service;
/////////////////
/**
* Returns the create zone form config
* @returns {{schema, form, model}|*}
*/
function getCreateFormConfig() {
return getCreateUpdateFormConfig(false);
}
/**
* Return the update zone form config
* @returns {{schema, form, model}|*}
*/
function getUpdateFormConfig() {
return getCreateUpdateFormConfig(true);
}
/**
* Return the create/update zone form. The two forms are identical except for
* during update, some fields are read-only.
*
* @param readonly - sets readonly value on form fields that change between update and create
* @returns {object} a schema form config, including default model
*/
function getCreateUpdateFormConfig(readonly) {
return {
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"pattern": /^.+\.$/
},
"description": {
"type": "string"
},
"email": {
"type": "string",
"format": "email",
"pattern": /^[^@]+@[^@]+$/
},
"type": {
"type": "string",
"enum": [
"PRIMARY",
"SECONDARY"
]
},
"ttl": {
"type": "integer",
"minimum": 1,
"maximum": 2147483647
},
"masters": {
"type": "array",
"items": {
"type": "object",
"properties": {
"address": {
"type": "string"
}
}
},
"minItems": 1,
"uniqueItems": true
}
}
},
"form": [
{
"key": "name",
"readonly": readonly,
"title": gettext("Name"),
"description": gettext("Zone name ending in '.'"),
"validationMessage": gettext("Zone must end with '.'"),
"placeholder": "example.com.",
"type": "text",
"required": true
},
{
"key": "description",
"type": "textarea",
"title": gettext("Description"),
"description": gettext("Details about the zone.")
},
{
"key": "email",
"title": gettext("Email Address"),
"placeholder": "owner@example.com",
"description": gettext("Email address to contact the zone owner."),
"validationMessage": gettext("Email address must contain a single '@' character"),
"type": "text",
"required": true
},
{
"key": "ttl",
"title": gettext("TTL"),
"description": gettext("Time To Live in seconds."),
"type": "number",
"required": true
},
{
"key": "type",
"readonly": readonly,
"title": gettext("Type"),
"description": gettext("Select the type of zone"),
"type": "select",
"titleMap": [
{
"value": "PRIMARY",
"name": gettext("Primary")
},
{
"value": "SECONDARY",
"name": gettext("Secondary")
}
]
},
{
"key": "masters",
"readonly": readonly,
"title": gettext("Masters"),
"type": "array",
"description": gettext("DNS master(s) for the Secondary zone."),
"condition": "model.type == 'SECONDARY'",
"add": gettext("Add Master"),
"items": [
{
"key": "masters[].address",
"title": gettext("IP Address")
}
]
}
],
"model": {
"type": "PRIMARY",
"ttl": 3600
}
};
}
}
})();

View File

@ -0,0 +1 @@
<h1>CREATE TEMPLATE</h1>

View File

@ -0,0 +1,126 @@
/**
*
* (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use self 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('designatedashboard.resources.os-designate-zone.actions')
.factory('designatedashboard.resources.os-designate-zone.actions.create', action);
action.$inject = [
'$q',
'designatedashboard.resources.os-designate-zone.actions.common-forms',
'designatedashboard.resources.os-designate-zone.api',
'designatedashboard.resources.os-designate-zone.resourceType',
'horizon.app.core.openstack-service-api.policy',
'horizon.app.core.openstack-service-api.serviceCatalog',
'horizon.framework.widgets.form.ModalFormService',
'horizon.framework.widgets.toast.service',
'horizon.framework.widgets.modal-wait-spinner.service'
];
/**
* @ngDoc factory
* @name designatedashboard.resources.os-designate-zone.actions.create
*
* @Description
* Brings up the Create Zone modal.
*/
function action($q,
forms,
api,
resourceTypeName,
policy,
serviceCatalog,
schemaFormModalService,
toast,
waitSpinner) {
var createZonePolicy, dnsServiceEnabled;
var title = gettext("Create Zone");
var message = {
success: gettext('Zone %s was successfully created.')
};
var service = {
initScope: initScope,
allowed: allowed,
perform: perform
};
return service;
/////////////////
function initScope() {
createZonePolicy = policy.ifAllowed({rules: [['dns', 'create_zone']]});
dnsServiceEnabled = serviceCatalog.ifTypeEnabled('dns');
}
function allowed() {
return $q.all([
createZonePolicy,
dnsServiceEnabled
]);
}
function perform() {
var formConfig = forms.getCreateFormConfig();
formConfig.title = title;
return schemaFormModalService.open(formConfig).then(onSubmit, onCancel);
}
function onSubmit(context) {
var zoneModel = angular.copy(context.model);
// schema form doesn't appear to support populating the masters array directly
// Map the masters objects to simple array of addresses
if (context.model.hasOwnProperty("masters")) {
var masters = context.model.masters.map(function (item) {
return item.address;
});
zoneModel.masters = masters;
}
waitSpinner.showModalSpinner(gettext('Creating Zone'));
return api.create(zoneModel).then(onSuccess, onFailure);
}
function onCancel() {
waitSpinner.hideModalSpinner();
}
function onSuccess(response) {
waitSpinner.hideModalSpinner();
var zone = response.data;
toast.add('success', interpolate(message.success, [zone.name]));
// To make the result of this action generically useful, reformat the return
// from the deleteModal into a standard form
return {
created: [{type: resourceTypeName, id: zone.id}],
updated: [],
deleted: [],
failed: []
};
}
function onFailure() {
waitSpinner.hideModalSpinner();
}
}
})();

View File

@ -0,0 +1,169 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use self 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('designatedashboard.resources.os-designate-zone.actions')
.factory('designatedashboard.resources.os-designate-zone.actions.delete', action);
action.$inject = [
'$q',
'designatedashboard.resources.os-designate-zone.api',
'designatedashboard.resources.util',
'horizon.app.core.openstack-service-api.policy',
'horizon.framework.util.actions.action-result.service',
'horizon.framework.util.i18n.gettext',
'horizon.framework.util.q.extensions',
'horizon.framework.widgets.modal.deleteModalService',
'horizon.framework.widgets.toast.service',
'designatedashboard.resources.os-designate-zone.resourceType'
];
/*
* @ngdoc factory
* @name designatedashboard.resources.os-designate-zone.actions.delete
*
* @Description
* Brings up the delete zone confirmation modal dialog.
* On submit, delete given zone.
* On cancel, do nothing.
*/
function action(
$q,
zoneApi,
util,
policy,
actionResultService,
gettext,
$qExtensions,
deleteModal,
toast,
resourceType
) {
var scope, context, deleteZonePromise;
var notAllowedMessage = gettext("You are not allowed to delete zones: %s");
var service = {
initScope: initScope,
allowed: allowed,
perform: perform
};
return service;
//////////////
function initScope(newScope) {
scope = newScope;
context = { };
deleteZonePromise = policy.ifAllowed({rules: [['dns', 'delete_zone']]});
}
function perform(items) {
var zones = angular.isArray(items) ? items : [items];
context.labels = labelize(zones.length);
context.deleteEntity = deleteZone;
return $qExtensions.allSettled(zones.map(checkPermission)).then(afterCheck);
}
function allowed(zone) {
// only row actions pass in zone
// otherwise, assume it is a batch action
if (zone) {
return $q.all([
deleteZonePromise,
util.notDeleted(zone),
util.notPending(zone)
]);
} else {
return policy.ifAllowed({ rules: [['dns', 'delete_zone']] });
}
}
function checkPermission(zone) {
return {promise: allowed(zone), context: zone};
}
function afterCheck(result) {
var outcome = $q.reject(); // Reject the promise by default
if (result.fail.length > 0) {
toast.add('error', getMessage(notAllowedMessage, result.fail));
outcome = $q.reject(result.fail);
}
if (result.pass.length > 0) {
outcome = deleteModal.open(scope, result.pass.map(getEntity), context).then(createResult);
}
return outcome;
}
function createResult(deleteModalResult) {
// To make the result of this action generically useful, reformat the return
// from the deleteModal into a standard form
var actionResult = actionResultService.getActionResult();
deleteModalResult.pass.forEach(function markDeleted(item) {
actionResult.deleted(resourceType, getEntity(item).id);
});
deleteModalResult.fail.forEach(function markFailed(item) {
actionResult.failed(resourceType, getEntity(item).id);
});
return actionResult.result;
}
function labelize(count) {
return {
title: ngettext(
'Confirm Delete Zone',
'Confirm Delete Zones', count),
message: ngettext(
'You have selected "%s". Deleted zone is not recoverable.',
'You have selected "%s". Deleted zones are not recoverable.', count),
submit: ngettext(
'Delete Zone',
'Delete Zones', count),
success: ngettext(
'Deleted Zone: %s.',
'Deleted Zones: %s.', count),
error: ngettext(
'Unable to delete Zone: %s.',
'Unable to delete Zones: %s.', count)
};
}
function deleteZone(zone) {
return zoneApi.deleteZone(zone, true);
}
function getMessage(message, entities) {
return interpolate(message, [entities.map(getName).join(", ")]);
}
function getName(result) {
return getEntity(result).name;
}
function getEntity(result) {
return result.context;
}
}
})();

View File

@ -0,0 +1,147 @@
/**
*
* (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use self 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('designatedashboard.resources.os-designate-zone.actions')
.factory('designatedashboard.resources.os-designate-zone.actions.update', action);
action.$inject = [
'$q',
'designatedashboard.resources.os-designate-zone.actions.common-forms',
'designatedashboard.resources.os-designate-zone.api',
'designatedashboard.resources.os-designate-zone.resourceType',
'designatedashboard.resources.util',
'horizon.app.core.openstack-service-api.policy',
'horizon.app.core.openstack-service-api.serviceCatalog',
'horizon.framework.widgets.form.ModalFormService',
'horizon.framework.widgets.toast.service',
'horizon.framework.widgets.modal-wait-spinner.service'
];
/**
* @ngDoc factory
* @name designatedashboard.resources.os-designate-zone.actions.update
*
* @Description
* Brings up the Update Zone modal.
*/
function action($q,
forms,
api,
resourceTypeName,
util,
policy,
serviceCatalog,
schemaFormModalService,
toast,
waitSpinner) {
var updateZonePolicy, dnsServiceEnabled;
var title = gettext("Update Zone");
var message = {
success: gettext('Zone %s was successfully updated.')
};
var service = {
initScope: initScope,
allowed: allowed,
perform: perform
};
return service;
/////////////////
function initScope() {
updateZonePolicy = policy.ifAllowed({rules: [['dns', 'update_zone']]});
dnsServiceEnabled = serviceCatalog.ifTypeEnabled('dns');
}
function allowed(zone) {
// only supports row action (exactly 1 zone)
if (zone) {
return $q.all([
updateZonePolicy,
util.notDeleted(zone),
util.notPending(zone)
]);
} else {
return false;
}
}
function perform(item) {
var formConfig = forms.getUpdateFormConfig();
formConfig.title = title;
formConfig.model = util.getModel(formConfig.form, item);
// Append the id so it can be used on submit
formConfig.model.id = item.id;
// schema form doesn't appear to support populating the masters array directly
// Map the masters objects to address objects
if (item.hasOwnProperty("masters")) {
var masters = item.masters.map(function (item) {
return { "address": item }
})
formConfig.masters = masters;
}
return schemaFormModalService.open(formConfig).then(onSubmit, onCancel);
}
function onSubmit(context) {
var zoneModel = angular.copy(context.model);
// schema form doesn't appear to support populating the masters array directly
// Map the masters objects to simple array of addresses
if (context.model.hasOwnProperty("masters")) {
var masters = context.model.masters.map(function (item) {
return item.address;
})
zoneModel.masters = masters;
}
waitSpinner.showModalSpinner(gettext('Updating Zone'));
return api.update(zoneModel.id, zoneModel).then(onSuccess, onFailure);
}
function onCancel() {
waitSpinner.hideModalSpinner();
}
function onSuccess(response) {
waitSpinner.hideModalSpinner();
var zone = response.data;
toast.add('success', interpolate(message.success, [zone.name]));
// To make the result of this action generically useful, reformat the return
// from the deleteModal into a standard form
return {
created: [],
updated: [{type: resourceTypeName, id: zone.id}],
deleted: [],
failed: []
};
}
function onFailure() {
waitSpinner.hideModalSpinner();
}
}
})();

View File

@ -0,0 +1,152 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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('designatedashboard.resources.os-designate-zone')
.factory('designatedashboard.resources.os-designate-zone.api', apiService);
apiService.$inject = [
'designatedashboard.apiPassthroughUrl',
'horizon.framework.util.http.service',
'horizon.framework.widgets.toast.service'
];
/**
* @ngdoc service
* @param {Object} httpService
* @param {Object} toastService
* @name apiService
* @description Provides direct access to Designate Zone APIs.
* @returns {Object} The service
*/
function apiService(apiPassthroughUrl, httpService, toastService) {
var service = {
get: get,
list: list,
deleteZone: deleteZone,
create: create,
update: update
};
return service;
///////////////
/**
* @name list
* @description
* Get a list of zones.
*
* The listing result is an object with property "items." Each item is
* a zone.
*
* @param {Object} params
* Query parameters. Optional.
*
* @returns {Object} The result of the API call
*/
/*
function list(params) {
var config = params ? {'params': params} : {};
return httpService.get('/api/designate/zones/', config)
.error(function () {
toastService.add('error', gettext('Unable to retrieve the zone.'));
});
}*/
function list(params) {
var config = params ? {'params': params} : {};
return httpService.get(apiPassthroughUrl + 'v2/zones/', config)
.error(function () {
toastService.add('error', gettext('Unable to retrieve the zone.'));
});
}
/**
* @name get
* @description
* Get a single zone by ID.
*
* @param {string} id
* Specifies the id of the zone to request.
*
* @returns {Object} The result of the API call
*/
function get(id) {
return httpService.get(apiPassthroughUrl + 'v2/zones/' + id + '/')
.error(function () {
toastService.add('error', gettext('Unable to retrieve the zone.'));
});
}
/**
* @name deleteZone
* @description
* Delete a single zone by ID
* @param id
* @returns {*}
*/
function deleteZone(id) {
return httpService.delete(apiPassthroughUrl + 'v2/zones/' + id + '/')
.error(function () {
toastService.add('error', gettext('Unable to delete the zone.'));
});
}
/**
* @name create
* @description
* Create a zone
*
* @param {Object} data
* Specifies the zone information to create
*
* @returns {Object} The created zone object
*/
function create(data) {
return httpService.post(apiPassthroughUrl + 'v2/zones/', data)
.error(function() {
toastService.add('error', gettext('Unable to create the zone.'));
})
}
/**
* @name create
* @description
* Update a zone
*
* @param {Object} id - zone id
* @param {Object} data to pass directly to zone update API
* Specifies the zone information to update
*
* @returns {Object} The updated zone object
*/
function update(id, data) {
// The update API will not accept extra data. Restrict the input to only the allowed
// fields
var apiData = {
email: data.email,
ttl: data.ttl,
description: data.description
};
return httpService.patch(apiPassthroughUrl + 'v2/zones/' + id + '/', apiData )
.error(function() {
toastService.add('error', gettext('Unable to update the zone.'));
})
}
}
}());

View File

@ -0,0 +1,61 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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
* @ngname designatedashboard.resources.os-designate-zone.details
*
* @description
* Provides details features for zones.
*/
angular.module('designatedashboard.resources.os-designate-zone.details',
['horizon.framework.conf', 'horizon.app.core'])
.run(run);
run.$inject = [
'designatedashboard.resources.os-designate-zone.resourceType',
'designatedashboard.resources.os-designate-zone.api',
'designatedashboard.resources.os-designate-zone.basePath',
'horizon.framework.conf.resource-type-registry.service'
];
function run(
zoneResourceType,
zoneApi,
basePath,
registry
) {
var resourceType = registry.getResourceType(zoneResourceType);
resourceType
.setLoadFunction(loadFunction)
.setSummaryTemplateUrl(basePath + 'details/drawer.html');
resourceType.detailsViews
.prepend({
id: 'zoneDetailsOverview',
name: gettext('Overview'),
template: basePath + 'details/overview.html',
}, 0);
function loadFunction(identifier) {
return zoneApi.get(identifier);
}
}
})();

View File

@ -0,0 +1,10 @@
<hz-resource-property-list
ng-if="item"
resource-type-name="OS::Designate::Zone"
item="item"
cls="dl-horizontal"
property-groups="[
['description', 'email'],
['id', 'pool_id', 'project_id']
]">
</hz-resource-property-list>

View File

@ -0,0 +1,46 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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('designatedashboard.resources.os-designate-zone')
.controller('designatedashboard.resources.os-designate-zone.detailController', controller);
controller.$inject = [
'designatedashboard.resources.os-designate-zone.resourceType',
'horizon.framework.conf.resource-type-registry.service',
'$scope'
];
function controller(
resourceTypeCode,
registry,
$scope
) {
var ctrl = this;
ctrl.item;
ctrl.resourceType = registry.getResourceType(resourceTypeCode);
$scope.context.loadPromise.then(onGetResponse);
function onGetResponse(response) {
ctrl.item = response.data;
}
}
})();

View File

@ -0,0 +1,58 @@
<div ng-controller="designatedashboard.resources.os-designate-zone.detailController as ctrl">
<div class="row">
<div class="col-md-6 detail">
<h3 translate>Details</h3>
<hr>
<hz-resource-property-list
ng-if="ctrl.item"
resource-type-name="OS::Designate::Zone"
item="ctrl.item"
cls="dl-horizontal"
property-groups="[
['id', 'description', 'type', 'status', 'action', 'email', 'serial', 'ttl', 'version']
]">
</hz-resource-property-list>
</div>
<div class="col-md-6 detail">
<h3 translate>Attributes</h3>
<hr>
<hz-resource-property-list
ng-if="ctrl.item"
resource-type-name="OS::Designate::Zone"
item="ctrl.item"
cls="dl-horizontal"
property-groups="[
['attributes']
]">
</hz-resource-property-list>
</div>
</div>
<div class="row">
<div class="col-md-6 detail">
<h3 translate>Modification Times</h3>
<hr>
<hz-resource-property-list
ng-if="ctrl.item"
resource-type-name="OS::Designate::Zone"
item="ctrl.item"
cls="dl-horizontal"
property-groups="[
['created_at', 'updated_at', 'transferred_at']
]">
</hz-resource-property-list>
</div>
<div class="col-md-6 detail">
<h3 translate>Associations</h3>
<hr>
<hz-resource-property-list
ng-if="ctrl.item"
resource-type-name="OS::Designate::Zone"
item="ctrl.item"
cls="dl-horizontal"
property-groups="[
['pool_id', 'project_id', 'masters']
]">
</hz-resource-property-list>
</div>
</div>
</div>

View File

@ -0,0 +1,202 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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
* @ngname designatedashboard.resources.os-designate-zone
*
* @description
* Provides all of the services and widgets required
* to support and display DNS (designate) zone related content.
*/
angular
.module('designatedashboard.resources.os-designate-zone', [
'ngRoute',
'designatedashboard.resources.os-designate-zone.actions',
'designatedashboard.resources.os-designate-zone.details'
])
.constant(
'designatedashboard.resources.os-designate-zone.resourceType',
'OS::Designate::Zone')
.config(config)
.run(run);
config.$inject = ['$provide', '$windowProvider'];
function config($provide, $windowProvider) {
var path = $windowProvider.$get().STATIC_URL + 'designatedashboard/resources/os-designate-zone/';
$provide.constant('designatedashboard.resources.os-designate-zone.basePath', path);
}
run.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'designatedashboard.resources.os-designate-zone.api',
'designatedashboard.resources.os-designate-zone.resourceType',
'designatedashboard.resources.util'
];
function run(registry,
zoneApi,
resourceTypeString,
util) {
var resourceType = registry.getResourceType(resourceTypeString);
resourceType
.setNames(gettext('DNS Zone'), gettext('DNS Zones'))
.setListFunction(listZones)
.setProperty('action', {
label: gettext('Action'),
filters: ['lowercase', 'noName'],
values: util.actionMap()
})
.setProperty('attributes', {
label: gettext('Attributes')
})
.setProperty('created_at', {
label: gettext('Created At'),
filters: ['noValue']
})
.setProperty('description', {
label: gettext('Description'),
filters: ['noName']
})
.setProperty('email', {
label: gettext('Email'),
filters: ['noName']
})
.setProperty('id', {
label: gettext('ID')
})
.setProperty('masters', {
label: gettext('Masters'),
filters: ['noValue']
})
.setProperty('name', {
label: gettext('Name'),
filters: ['noName']
})
.setProperty('pool_id', {
label: gettext('Pool ID')
})
.setProperty('project_id', {
label: gettext('Project ID')
})
.setProperty('serial', {
label: gettext('Serial'),
filters: ['noValue']
})
.setProperty('status', {
label: gettext('Status'),
filters: ['lowercase', 'noName'],
values: util.statusMap()
})
.setProperty('transferred_at', {
label: gettext('Transferred At'),
filters: ['noValue']
})
.setProperty('ttl', {
label: gettext('Time To Live'),
filters: ['noValue']
})
.setProperty('type', {
label: gettext('Type'),
filters: ['lowercase', 'noName'],
values: typeMap()
})
.setProperty('updated_at', {
label: gettext('Updated At'),
filters: ['noValue']
})
.setProperty('version', {
label: gettext('Version'),
filters: ['noValue']
});
resourceType
.tableColumns
.append({
id: 'name',
priority: 1,
sortDefault: true,
template: '<a ng-href="{$ \'project/ngdetails/OS::Designate::Zone/\' + item.id $}">{$ item.name $}</a>'
})
.append({
id: 'type',
filters: ['lowercase'],
values: typeMap(),
priority: 2
})
.append({
id: 'status',
filters: ['lowercase'],
values: util.statusMap(),
priority: 2
});
resourceType
.filterFacets
.append({
label: gettext('Name'),
name: 'name',
isServer: false,
singleton: true,
persistent: false
})
.append({
label: gettext('Type'),
name: 'type',
isServer: false,
singleton: true,
persistent: false,
options: [
{label: gettext('Primary'), key: 'primary'},
{label: gettext('Secondary'), key: 'secondary'}
]
})
.append({
label: gettext('Status'),
name: 'status',
isServer: false,
singleton: true,
persistent: false,
options: [
{label: gettext('Active'), key: 'active'},
{label: gettext('Pending'), key: 'pending'}
]
});
function typeMap() {
return {
'primary': gettext('Primary'),
'secondary': gettext('Secondary')
}
}
function listZones() {
return zoneApi.list().then(function onList(response) {
// listFunctions are expected to return data in "items"
response.data.items = response.data.zones;
util.addTimestampIds(response.data.items, 'updated_at');
return response;
});
}
}
})();

View File

@ -0,0 +1,36 @@
/*
* (c) Copyright 2016 Hewlett-Packard Development Company, L.P.
*
* 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 designatedashboard.resources
* @description
*
* # designatedashboard.resources
*
* This module hosts registered resource types. This module file may
* contain individual registrations, or may have sub-modules that
* more fully contain registrations.
*/
angular
.module('designatedashboard.resources', [
'designatedashboard.resources.os-designate-recordset',
'designatedashboard.resources.os-designate-zone',
'designatedashboard.resources.os-designate-floatingip'
]);
})();

View File

@ -0,0 +1,107 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development LP
*
* 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('designatedashboard.resources')
.factory('designatedashboard.resources.util', utilService);
utilService.$inject = [
'horizon.framework.util.q.extensions',
];
function utilService($qExtensions) {
var service = {
notDeleted: notDeleted,
notPending: notPending,
getModel: getModel,
actionMap: actionMap,
statusMap: statusMap,
addTimestampIds: addTimestampIds
};
return service;
///////////////
function notDeleted(resource) {
return $qExtensions.booleanAsPromise(resource.status !== 'DELETED');
}
function notPending(resource) {
return $qExtensions.booleanAsPromise(resource.status !== 'PENDING');
}
/**
* Build a model object based on the given item, using only the fields present in the form config 'key's.
* Only 'truthy' values are copied.
*
* @param form - an array of objects describing the form. Must have a 'key' attribute.
* @param item - the data to copy into the model
*/
function getModel(form, item) {
var result = {};
var value;
form.forEach(function iterateForm(formItem) {
value = item[formItem.key];
if (value) {
result[formItem.key] = item[formItem.key];
}
});
return result;
}
function actionMap() {
return {
'none': gettext('None'),
'create': gettext('Create')
}
}
function statusMap() {
return {
'active': gettext('Active'),
'pending': gettext('Pending')
}
}
/**
* hz-resource-table tracks by 'id' which doesn't change when an individual item is updated.
* Create a synthetic '_timestampId' using the item id plus the specified timestamp field.
* When this field is used as a track-by in hz-resource-table, items in the table to update
* after row actions.
*
* If a timestamp field is not specified, the current time is used.
*
* @param items {object} - The items to add a _timestampId.
* @param idField {string} - (Optional) A field on the item to use as the id. Defaults to 'id'
* @param timestampField {string} - (Optional) A field on item to use as a timestamp. Defaults
* to current time.
*/
function addTimestampIds(items, idField, timestampField) {
var _idField = idField || 'id';
var timestamp = Date.now();
items.map(function annotateFloatingIp(item) {
if ( angular.isDefined(timestampField) ) {
timestamp = item[timestampField];
}
item._timestampId = item[_idField] + timestamp;
});
}
}
}());

View File

@ -0,0 +1,4 @@
<hz-resource-panel resource-type-name="OS::Designate::FloatingIp">
<hz-resource-table resource-type-name="OS::Designate::FloatingIp"
track-by="_timestampId"></hz-resource-table>
</hz-resource-panel>

View File

@ -0,0 +1,4 @@
<hz-resource-panel resource-type-name="OS::Designate::Zone">
<hz-resource-table resource-type-name="OS::Designate::Zone"
track-by="_timestampId"></hz-resource-table>
</hz-resource-panel>