diff --git a/README.rst b/README.rst index 74e089b..e2f3b8e 100644 --- a/README.rst +++ b/README.rst @@ -48,7 +48,7 @@ Howto and should be copied to /usr/share/openstack-dashboard/openstack_dashboard/local/enabled or the equivalent directory for your openstack-dashboard install. -3. Make sure your keystone catalog contains endpoints for service type 'dns'. If no such endpoints are +3. Make sure your keystone catalog contains endpoints for service type 'ssh'. If no such endpoints are found, the tatudashboard panels will not render. 4. (Optional) Copy the tatu policy file into horizon's policy files folder, and add this config:: diff --git a/tatudashboard/api/__init__.py b/tatudashboard/api/__init__.py index e334e49..7d3b2dc 100644 --- a/tatudashboard/api/__init__.py +++ b/tatudashboard/api/__init__.py @@ -1 +1,16 @@ -from tatudashboard.api import tatu # noqa +# (c) Copyright 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 tatudashboard.api import passthrough diff --git a/tatudashboard/api/rest/passthrough.py b/tatudashboard/api/passthrough.py similarity index 100% rename from tatudashboard/api/rest/passthrough.py rename to tatudashboard/api/passthrough.py diff --git a/tatudashboard/api/rest/__init__.py b/tatudashboard/api/rest/__init__.py deleted file mode 100644 index 6e741ff..0000000 --- a/tatudashboard/api/rest/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# (c) Copyright 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 diff --git a/tatudashboard/api/tatu.py b/tatudashboard/api/tatu.py deleted file mode 100644 index aa90126..0000000 --- a/tatudashboard/api/tatu.py +++ /dev/null @@ -1,171 +0,0 @@ -# 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 __future__ import absolute_import - -from keystoneauth1 import session as keystone_session -from keystoneauth1.identity import v3 - -from django.conf import settings # noqa - -from horizon import exceptions - -from openstack_dashboard.api.base import url_for # noqa -from oslo_log import log as logging - -LOG = logging.getLogger(__name__) - - -def designateclient(request): - designate_url = "" - try: - designate_url = url_for(request, 'dns') - except exceptions.ServiceCatalogException: - LOG.debug('no dns service configured.') - return None - - insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) - cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None) - - return Client(endpoint=designate_url, - token=request.user.token.id, - username=request.user.username, - tenant_id=request.user.project_id, - insecure=insecure, - cacert=cacert) - - -def domain_get(request, domain_id): - d_client = designateclient(request) - if d_client is None: - return [] - return d_client.domains.get(domain_id) - - -def domain_list(request): - d_client = designateclient(request) - if d_client is None: - return [] - return d_client.domains.list() - - -def domain_create(request, name, email, ttl=None, description=None): - d_client = designateclient(request) - if d_client is None: - return None - - options = { - 'description': description, - } - - # TTL needs to be optionally added as argument because the client - # won't accept a None value - if ttl is not None: - options['ttl'] = ttl - - domain = Domain(name=name, email=email, **options) - - return d_client.domains.create(domain) - - -def domain_update(request, domain_id, email, ttl, description=None): - d_client = designateclient(request) - if d_client is None: - return None - - # A quirk of the designate client is that you need to start with a - # base record and then update individual fields in order to persist - # the data. The designate client will only send the 'changed' fields. - domain = Domain(id=domain_id, name='', email='') - - domain.email = email - domain.ttl = ttl - domain.description = description - - return d_client.domains.update(domain) - - -def domain_delete(request, domain_id): - d_client = designateclient(request) - if d_client is None: - return [] - return d_client.domains.delete(domain_id) - - -def server_list(request, domain_id): - d_client = designateclient(request) - if d_client is None: - return [] - return d_client.domains.list_domain_servers(domain_id) - - -def record_list(request, domain_id): - d_client = designateclient(request) - if d_client is None: - return [] - return d_client.records.list(domain_id) - - -def record_get(request, domain_id, record_id): - d_client = designateclient(request) - if d_client is None: - return [] - return d_client.records.get(domain_id, record_id) - - -def record_delete(request, domain_id, record_id): - d_client = designateclient(request) - if d_client is None: - return [] - return d_client.records.delete(domain_id, record_id) - - -def record_create(request, domain_id, **kwargs): - d_client = designateclient(request) - if d_client is None: - return [] - - record = Record(**kwargs) - return d_client.records.create(domain_id, record) - - -def record_update(request, domain_id, record_id, **kwargs): - d_client = designateclient(request) - if d_client is None: - return [] - - # A quirk of the designate client is that you need to start with a - # base record and then update individual fields in order to persist - # the data. The designate client will only send the 'changed' fields. - record = Record( - id=record_id, - type='A', - name='', - data='') - - record.type = kwargs.get('type', None) - record.name = kwargs.get('name', None) - record.data = kwargs.get('data', None) - record.priority = kwargs.get('priority', None) - record.ttl = kwargs.get('ttl', None) - record.description = kwargs.get('description', None) - - return d_client.records.update(domain_id, record) - - -def quota_get(request, project_id=None): - if not project_id: - project_id = request.user.project_id - d_client = designateclient(request) - return d_client.quotas.get(project_id) diff --git a/tatudashboard/dashboards/project/hosts/panel.py b/tatudashboard/dashboards/project/hosts/panel.py index 2dd14a3..1d565d3 100644 --- a/tatudashboard/dashboards/project/hosts/panel.py +++ b/tatudashboard/dashboards/project/hosts/panel.py @@ -19,7 +19,7 @@ from openstack_dashboard.dashboards.project import dashboard class Hosts(horizon.Panel): name = _("Hosts") - slug = 'ssh_hosts' + slug = 'hosts' permissions = ('openstack.services.ssh',) dashboard.Project.register(Hosts) diff --git a/tatudashboard/enabled/_1722_ssh_hosts_panel.py b/tatudashboard/enabled/_1722_ssh_hosts_panel.py index 0f70ae1..539a55c 100644 --- a/tatudashboard/enabled/_1722_ssh_hosts_panel.py +++ b/tatudashboard/enabled/_1722_ssh_hosts_panel.py @@ -15,7 +15,7 @@ from tatudashboard import exceptions # The name of the panel to be added to HORIZON_CONFIG. Required. -PANEL = 'ssh_hosts' +PANEL = 'hosts' # The name of the dashboard the PANEL associated with. Required. PANEL_DASHBOARD = 'project' # The name of the panel group the PANEL is associated with. @@ -27,6 +27,8 @@ ADD_EXCEPTIONS = { 'unauthorized': exceptions.UNAUTHORIZED, } +ADD_INSTALLED_APPS = ['tatudashboard'] + # Python panel class of the PANEL to be added. ADD_PANEL = ( 'tatudashboard.dashboards.project.hosts.panel.Hosts') diff --git a/tatudashboard/static/tatudashboard/ssh_hosts.html b/tatudashboard/static/tatudashboard/hosts.html similarity index 100% rename from tatudashboard/static/tatudashboard/ssh_hosts.html rename to tatudashboard/static/tatudashboard/hosts.html diff --git a/tatudashboard/static/tatudashboard/resources/os-tatu-host/actions/common-forms.service.js b/tatudashboard/static/tatudashboard/resources/os-tatu-host/actions/common-forms.service.js index b859d5a..93af572 100644 --- a/tatudashboard/static/tatudashboard/resources/os-tatu-host/actions/common-forms.service.js +++ b/tatudashboard/static/tatudashboard/resources/os-tatu-host/actions/common-forms.service.js @@ -71,113 +71,48 @@ "properties": { "hostname": { "type": "string", - "pattern": /^.+\.$/ }, - "description": { - "type": "string" - }, - "email": { + "instance_id": { "type": "string", - "format": "email", - "pattern": /^[^@]+@[^@]+$/ }, - "type": { + "proj_id": { "type": "string", - "enum": [ - "PRIMARY", - "SECONDARY" - ] }, - "ttl": { - "type": "integer", - "minimum": 1, - "maximum": 2147483647 + "proj_name": { + "type": "string", }, - "masters": { - "type": "array", - "items": { - "type": "object", - "properties": { - "address": { - "type": "string" - } - } - }, - "minItems": 1, - "uniqueItems": true + "cert": { + "type": "string", + }, + "pat": { + "type": "string", + }, + "srv_url": { + "type": "string", } } }, "form": [ { - "key": "name", + "key": "hostname", "readonly": readonly, - "title": gettext("Name"), - "description": gettext("Host name ending in '.'"), - "validationMessage": gettext("Host must end with '.'"), - "placeholder": "example.com.", + "title": gettext("Hostname"), + "description": gettext("Foobar fudd"), "type": "text", "required": true }, { - "key": "description", - "type": "textarea", - "title": gettext("Description"), - "description": gettext("Details about the host.") - }, - { - "key": "email", - "title": gettext("Email Address"), - "description": gettext("Email address to contact the host owner."), - "validationMessage": gettext("Email address must contain a single '@' character"), + "key": "proj_name", + "readonly": readonly, + "title": gettext("Project Name"), + "description": gettext("Confounded JS noobie."), "type": "text", - "condition": "model.type == 'PRIMARY'", "required": true - }, - { - "key": "ttl", - "title": gettext("TTL"), - "description": gettext("Time To Live in seconds."), - "type": "number", - "condition": "model.type == 'PRIMARY'", - "required": true - }, - { - "key": "type", - "readonly": readonly, - "title": gettext("Type"), - "description": gettext("Select the type of host"), - "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 host."), - "condition": "model.type == 'SECONDARY'", - "add": gettext("Add Master"), - "items": [ - { - "key": "masters[].address", - "title": gettext("IP Address") - } - ] } ], "model": { - "type": "PRIMARY", - "ttl": 3600 + "proj_name": "Project24", + "hostname": "pluto" } }; } diff --git a/tatudashboard/static/tatudashboard/resources/os-tatu-host/api.service.js b/tatudashboard/static/tatudashboard/resources/os-tatu-host/api.service.js index e5fb184..e2faf04 100644 --- a/tatudashboard/static/tatudashboard/resources/os-tatu-host/api.service.js +++ b/tatudashboard/static/tatudashboard/resources/os-tatu-host/api.service.js @@ -21,7 +21,6 @@ .factory('tatudashboard.resources.os-tatu-host.api', apiService); apiService.$inject = [ - '$q', 'tatudashboard.apiPassthroughUrl', 'horizon.framework.util.http.service', 'horizon.framework.widgets.toast.service' @@ -35,7 +34,7 @@ * @description Provides direct access to Tatu Host APIs. * @returns {Object} The service */ - function apiService($q, apiPassthroughUrl, httpService, toastService) { + function apiService(apiPassthroughUrl, httpService, toastService) { var service = { get: get, list: list, @@ -51,7 +50,7 @@ /** * @name list * @description - * Get a list of record sets. + * Get a list of hosts. * * The listing result is an object with property "items." Each item is * a host. @@ -62,10 +61,38 @@ * @returns {Object} The result of the API call */ function list(params) { - return httpService.get(apiPassthroughUrl + 'hosts/', params) + var config = params ? {'params': params} : {}; + var hosts = [{ + 'instance_id': '00000000-aaaa-bbbb-cccc-111122224444', + 'hostname': 'fluffy', + 'proj_id': 'aaaaaaaa-aaaa-bbbb-cccc-111122224444', + 'proj_name': 'ProjectA', + 'cert': 'Bogus ssh cert...', + 'pat': '11.11.0.1:1002,11.11.0.2:1002', + 'srv_url': '_ssh._tcp.fluffy.aaaaaaaa.tatu.com.', + },{ + 'instance_id': '11111111-aaaa-bbbb-cccc-111122224444', + 'hostname': 'chester', + 'proj_id': 'aaaaaaaa-aaaa-bbbb-cccc-111122224444', + 'proj_name': 'ProjectA', + 'cert': 'Bogus ssh cert...', + 'pat': '11.11.0.1:1005,11.11.0.2:1005', + 'srv_url': '_ssh._tcp.chester.aaaaaaaa.tatu.com.', + },{ + 'instance_id': '22222222-aaaa-bbbb-cccc-111122224444', + 'hostname': 'snoopy', + 'proj_id': 'aaaaaaaa-aaaa-bbbb-cccc-111122224444', + 'proj_name': 'ProjectA', + 'cert': 'Bogus ssh cert...', + 'pat': '11.11.0.1:1009,11.11.0.2:1009', + 'srv_url': '_ssh._tcp.snoopy.aaaaaaaa.tatu.com.', + }]; + return hosts; + /* + return httpService.get(apiPassthroughUrl + 'hosts/', config) .error(function () { - toastService.add('error', gettext('Unable to retrieve the hosts.')); - }); + toastService.add('error', gettext('Unable to retrieve the host.')); + });*/ } /** @@ -73,61 +100,72 @@ * @description * Get a single host by ID. * - * @param {string} hostId + * @param {string} id * Specifies the id of the host to request. * * @returns {Object} The result of the API call */ - function get(hostId) { - // 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 + 'hosts/' + hostId + '/') - .then(undefined, function onError() { + function get(id) { + return httpService.get(apiPassthroughUrl + 'hosts/' + id + '/') + .error(function () { toastService.add('error', gettext('Unable to retrieve the host.')); - return $q.when({}); }); } /** - * @name delete + * @name deleteHost * @description * Delete a single host by ID - * @param {string} zoneId - * The id of the zone containing the host - * - * @param {string} hostId - * The id of the host within the zone - * + * @param id * @returns {*} */ - function deleteRecordSet(zoneId, recordSetId) { - return httpService.delete(apiPassthroughUrl + 'v2/zones/' + zoneId + '/recordsets/' + recordSetId + '/') + function deleteHost(id) { + return httpService.delete(apiPassthroughUrl + 'hosts/' + id + '/') .error(function () { toastService.add('error', gettext('Unable to delete the host.')); }); } - function create(zoneId, data) { - return httpService.post(apiPassthroughUrl + 'v2/zones/' + zoneId + '/recordsets/', data) - .error(function () { + /** + * @name create + * @description + * Create a host + * + * @param {Object} data + * Specifies the host information to create + * + * @returns {Object} The created host object + */ + function create(data) { + return httpService.post(apiPassthroughUrl + 'hosts/', data) + .error(function() { toastService.add('error', gettext('Unable to create the host.')); - }); + }) } - function update(zoneId, recordSetId, data) { + /** + * @name create + * @description + * Update a host + * + * @param {Object} id - host id + * @param {Object} data to pass directly to host update API + * Specifies the host information to update + * + * @returns {Object} The updated host 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, - records: data.records + description: data.description }; - return httpService.put(apiPassthroughUrl + 'v2/zones/' + zoneId + '/recordsets/' + recordSetId, apiData) - .error(function () { + return httpService.patch(apiPassthroughUrl + 'hosts/' + id + '/', apiData ) + .error(function() { toastService.add('error', gettext('Unable to update the host.')); - }); + }) } } }()); diff --git a/tatudashboard/static/tatudashboard/resources/os-tatu-host/os-tatu-host.module.js b/tatudashboard/static/tatudashboard/resources/os-tatu-host/os-tatu-host.module.js index c2d927b..7bb03f4 100644 --- a/tatudashboard/static/tatudashboard/resources/os-tatu-host/os-tatu-host.module.js +++ b/tatudashboard/static/tatudashboard/resources/os-tatu-host/os-tatu-host.module.js @@ -32,24 +32,22 @@ 'tatudashboard.resources.os-tatu-host.details' ]) .constant( - 'tatudashboard.resources.os-tatu-host.resourceType', - 'OS::Tatu::Host') + 'tatudashboard.resources.os-tatu-host.resourceType', 'OS::Tatu::Host') .config(config) .run(run); config.$inject = ['$provide', '$windowProvider']; function config($provide, $windowProvider) { - var path = $windowProvider.$get().STATIC_URL + 'tatudashboard/resources/os-tatu-recordset/'; - $provide.constant('tatudashboard.resources.os-tatu-recordset.basePath', path); + var path = $windowProvider.$get().STATIC_URL + 'tatudashboard/resources/os-tatu-host/'; + $provide.constant('tatudashboard.resources.os-tatu-host.basePath', path); } run.$inject = [ 'horizon.app.core.detailRoute', 'horizon.framework.conf.resource-type-registry.service', - 'tatudashboard.resources.os-tatu-recordset.api', - 'tatudashboard.resources.os-tatu-recordset.resourceType', - 'tatudashboard.resources.os-tatu-recordset.typeMap', + 'tatudashboard.resources.os-tatu-host.api', + 'tatudashboard.resources.os-tatu-host.resourceType', 'tatudashboard.resources.util' ]; @@ -57,26 +55,26 @@ registry, hostApi, resourceTypeString, - typeMap, util) { var resourceType = registry.getResourceType(resourceTypeString); resourceType .setNames(gettext('SSH Host'), gettext('SSH Hosts')) - .setListFunction(list) + .setListFunction(listHosts) + .setProperty('action', { + label: gettext('Action'), + values: util.actionMap() + }) .setProperty('instance_id', { label: gettext('Instance ID') }) .setProperty('hostname', { label: gettext('Hostname'), - filters: ['noName'] }) .setProperty('proj_id', { label: gettext('Project ID'), - filters: ['noValue'] }) .setProperty('proj_name', { label: gettext('Project Name'), - filters: ['noValue'] }) .setProperty('cert', { label: gettext('Certificate') @@ -91,10 +89,10 @@ resourceType .tableColumns .append({ - id: 'host_name', + id: 'hostname', priority: 1, sortDefault: true, - template: '{$ item.name $}' + template: '{$ item.hostname $}' }) .append({ id: 'proj_name', @@ -112,8 +110,15 @@ resourceType .filterFacets .append({ - label: gettext('Name'), - name: 'name', + label: gettext('Hostname'), + name: 'hostname', + isServer: false, + singleton: true, + persistent: false + }) + .append({ + label: gettext('Project'), + name: 'proj_name', isServer: false, singleton: true, persistent: false diff --git a/tatudashboard/static/tatudashboard/tatudashboard.module.js b/tatudashboard/static/tatudashboard/tatudashboard.module.js index d3aa1b8..fcf39b8 100644 --- a/tatudashboard/static/tatudashboard/tatudashboard.module.js +++ b/tatudashboard/static/tatudashboard/tatudashboard.module.js @@ -58,7 +58,7 @@ $provide.constant('tatudashboard.basePath', path); $routeProvider - .when('/project/ssh_hosts/', { + .when('/project/hosts/', { templateUrl: path + 'hosts.html' }); }