Debugging SSH Hosts panel.
This commit is contained in:
parent
ae759df379
commit
e3570734fa
@ -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::
|
||||
|
@ -1 +1,16 @@
|
||||
from tatudashboard.api import tatu # noqa
|
||||
# (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 tatudashboard.api import passthrough
|
||||
|
@ -1,16 +0,0 @@
|
||||
# (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
|
@ -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)
|
@ -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)
|
||||
|
@ -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')
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
"cert": {
|
||||
"type": "string",
|
||||
},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
"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"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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.'));
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
}());
|
||||
|
@ -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: '<a ng-href="{$ \'' + detailRoute + 'OS::Tatu::Host/\' + item.id $}">{$ item.name $}</a>'
|
||||
template: '<a ng-href="{$ \'' + detailRoute + 'OS::Tatu::Host/\' + item.instance_id $}">{$ item.hostname $}</a>'
|
||||
})
|
||||
.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
|
||||
|
@ -58,7 +58,7 @@
|
||||
$provide.constant('tatudashboard.basePath', path);
|
||||
|
||||
$routeProvider
|
||||
.when('/project/ssh_hosts/', {
|
||||
.when('/project/hosts/', {
|
||||
templateUrl: path + 'hosts.html'
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user