
There are no longer two separate projects living inside the horizon repository. There is a single project now with a single setup.py, single README, etc. The openstack-dashboard/dashboard django project is now named "openstack_dashboard" and lives as an example project in the topmost horizon directory. The "horizon/horizon" directory has been bumped up a level and now is directly on the path when the root horizon directory is on your python path. Javascript media which the horizon module directly relies upon now ships in the horizon/static dir rather than openstack-dashboard/dashboard/static. All the corresponding setup, installation, build, and env scripts have been updated accordingly. Implements blueprint unified-packaging. Change-Id: Ieed8e3c777432cd046c3e0298869a9428756ab62
483 lines
16 KiB
Python
483 lines
16 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright 2012 United States Government as represented by the
|
|
# Administrator of the National Aeronautics and Space Administration.
|
|
# All Rights Reserved.
|
|
#
|
|
# Copyright 2012 Openstack, LLC
|
|
# Copyright 2012 Nebula, Inc.
|
|
# Copyright (c) 2012 X.commerce, a business unit of eBay Inc.
|
|
#
|
|
# 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
|
|
|
|
import logging
|
|
|
|
from novaclient.v1_1 import client as nova_client
|
|
from novaclient.v1_1 import security_group_rules as nova_rules
|
|
from novaclient.v1_1.servers import REBOOT_HARD
|
|
|
|
from horizon.api import keystone
|
|
from horizon.api.base import APIResourceWrapper, APIDictWrapper, url_for
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
# API static values
|
|
INSTANCE_ACTIVE_STATE = 'ACTIVE'
|
|
VOLUME_STATE_AVAILABLE = "available"
|
|
|
|
|
|
class VNCConsole(APIDictWrapper):
|
|
"""Wrapper for the "console" dictionary returned by the
|
|
novaclient.servers.get_vnc_console method.
|
|
"""
|
|
_attrs = ['url', 'type']
|
|
|
|
|
|
class Quota(object):
|
|
"""Wrapper for individual limits in a quota."""
|
|
def __init__(self, name, limit):
|
|
self.name = name
|
|
self.limit = limit
|
|
|
|
def __repr__(self):
|
|
return "<Quota: (%s, %s)>" % (self.name, self.limit)
|
|
|
|
|
|
class QuotaSet(object):
|
|
"""Wrapper for novaclient.quotas.QuotaSet objects which wraps the
|
|
individual quotas inside Quota objects.
|
|
"""
|
|
def __init__(self, apiresource):
|
|
self.items = []
|
|
for k in apiresource._info.keys():
|
|
if k in ['id']:
|
|
continue
|
|
v = int(apiresource._info[k])
|
|
q = Quota(k, v)
|
|
self.items.append(q)
|
|
setattr(self, k, v)
|
|
|
|
|
|
class Server(APIResourceWrapper):
|
|
"""Simple wrapper around novaclient.server.Server
|
|
|
|
Preserves the request info so image name can later be retrieved
|
|
|
|
"""
|
|
_attrs = ['addresses', 'attrs', 'id', 'image', 'links',
|
|
'metadata', 'name', 'private_ip', 'public_ip', 'status', 'uuid',
|
|
'image_name', 'VirtualInterfaces', 'flavor', 'key_name',
|
|
'tenant_id', 'user_id', 'OS-EXT-STS:power_state',
|
|
'OS-EXT-STS:task_state', 'OS-EXT-SRV-ATTR:instance_name',
|
|
'OS-EXT-SRV-ATTR:host']
|
|
|
|
def __init__(self, apiresource, request):
|
|
super(Server, self).__init__(apiresource)
|
|
self.request = request
|
|
|
|
@property
|
|
def image_name(self):
|
|
from glance.common import exception as glance_exceptions
|
|
from horizon.api import glance
|
|
try:
|
|
image = glance.image_get_meta(self.request, self.image['id'])
|
|
return image.name
|
|
except glance_exceptions.NotFound:
|
|
return "(not found)"
|
|
|
|
def reboot(self, hardness=REBOOT_HARD):
|
|
novaclient(self.request).servers.reboot(self.id, hardness)
|
|
|
|
|
|
class Usage(APIResourceWrapper):
|
|
"""Simple wrapper around contrib/simple_usage.py."""
|
|
_attrs = ['start', 'server_usages', 'stop', 'tenant_id',
|
|
'total_local_gb_usage', 'total_memory_mb_usage',
|
|
'total_vcpus_usage', 'total_hours']
|
|
|
|
def get_summary(self):
|
|
return {'instances': self.total_active_instances,
|
|
'memory_mb': self.memory_mb,
|
|
'vcpus': getattr(self, "total_vcpus_usage", 0),
|
|
'vcpu_hours': self.vcpu_hours,
|
|
'local_gb': self.local_gb,
|
|
'disk_gb_hours': self.disk_gb_hours}
|
|
|
|
@property
|
|
def total_active_instances(self):
|
|
return sum(1 for s in self.server_usages if s['ended_at'] == None)
|
|
|
|
@property
|
|
def vcpus(self):
|
|
return sum(s['vcpus'] for s in self.server_usages
|
|
if s['ended_at'] == None)
|
|
|
|
@property
|
|
def vcpu_hours(self):
|
|
return getattr(self, "total_hours", 0)
|
|
|
|
@property
|
|
def local_gb(self):
|
|
return sum(s['local_gb'] for s in self.server_usages
|
|
if s['ended_at'] == None)
|
|
|
|
@property
|
|
def memory_mb(self):
|
|
return sum(s['memory_mb'] for s in self.server_usages
|
|
if s['ended_at'] == None)
|
|
|
|
@property
|
|
def disk_gb_hours(self):
|
|
return getattr(self, "total_local_gb_usage", 0)
|
|
|
|
|
|
class SecurityGroup(APIResourceWrapper):
|
|
"""Wrapper around novaclient.security_groups.SecurityGroup which wraps its
|
|
rules in SecurityGroupRule objects and allows access to them.
|
|
"""
|
|
_attrs = ['id', 'name', 'description', 'tenant_id']
|
|
|
|
@property
|
|
def rules(self):
|
|
"""Wraps transmitted rule info in the novaclient rule class."""
|
|
if not hasattr(self, "_rules"):
|
|
manager = nova_rules.SecurityGroupRuleManager
|
|
self._rules = [nova_rules.SecurityGroupRule(manager, rule) for \
|
|
rule in self._apiresource.rules]
|
|
return self._rules
|
|
|
|
@rules.setter
|
|
def rules(self, value):
|
|
self._rules = value
|
|
|
|
|
|
class SecurityGroupRule(APIResourceWrapper):
|
|
""" Wrapper for individual rules in a SecurityGroup. """
|
|
_attrs = ['id', 'ip_protocol', 'from_port', 'to_port', 'ip_range']
|
|
|
|
def __unicode__(self):
|
|
vals = {'from': self.from_port,
|
|
'to': self.to_port,
|
|
'cidr': self.ip_range['cidr']}
|
|
return 'ALLOW %(from)s:%(to)s from %(cidr)s' % vals
|
|
|
|
|
|
def novaclient(request):
|
|
LOG.debug('novaclient connection created using token "%s" and url "%s"' %
|
|
(request.user.token, url_for(request, 'compute')))
|
|
c = nova_client.Client(request.user.username,
|
|
request.user.token,
|
|
project_id=request.user.tenant_id,
|
|
auth_url=url_for(request, 'compute'))
|
|
c.client.auth_token = request.user.token
|
|
c.client.management_url = url_for(request, 'compute')
|
|
return c
|
|
|
|
|
|
def cinderclient(request):
|
|
LOG.debug('cinderclient connection created using token "%s" and url "%s"' %
|
|
(request.user.token, url_for(request, 'volume')))
|
|
c = nova_client.Client(request.user.username,
|
|
request.user.token,
|
|
project_id=request.user.tenant_id,
|
|
auth_url=url_for(request, 'volume'))
|
|
c.client.auth_token = request.user.token
|
|
c.client.management_url = url_for(request, 'volume')
|
|
return c
|
|
|
|
|
|
def server_vnc_console(request, instance_id, console_type='novnc'):
|
|
return VNCConsole(novaclient(request).servers.get_vnc_console(instance_id,
|
|
console_type)['console'])
|
|
|
|
|
|
def flavor_create(request, name, memory, vcpu, disk, flavor_id, ephemeral=0):
|
|
return novaclient(request).flavors.create(name, int(memory), int(vcpu),
|
|
int(disk), flavor_id,
|
|
ephemeral=int(ephemeral))
|
|
|
|
|
|
def flavor_delete(request, flavor_id):
|
|
novaclient(request).flavors.delete(flavor_id)
|
|
|
|
|
|
def flavor_get(request, flavor_id):
|
|
return novaclient(request).flavors.get(flavor_id)
|
|
|
|
|
|
def flavor_list(request):
|
|
return novaclient(request).flavors.list()
|
|
|
|
|
|
def tenant_floating_ip_list(request):
|
|
"""Fetches a list of all floating ips."""
|
|
return novaclient(request).floating_ips.list()
|
|
|
|
|
|
def floating_ip_pools_list(request):
|
|
"""Fetches a list of all floating ip pools."""
|
|
return novaclient(request).floating_ip_pools.list()
|
|
|
|
|
|
def tenant_floating_ip_get(request, floating_ip_id):
|
|
"""Fetches a floating ip."""
|
|
return novaclient(request).floating_ips.get(floating_ip_id)
|
|
|
|
|
|
def tenant_floating_ip_allocate(request, pool=None):
|
|
"""Allocates a floating ip to tenant. Optionally you may provide a pool
|
|
for which you would like the IP.
|
|
"""
|
|
return novaclient(request).floating_ips.create(pool=pool)
|
|
|
|
|
|
def tenant_floating_ip_release(request, floating_ip_id):
|
|
"""Releases floating ip from the pool of a tenant."""
|
|
return novaclient(request).floating_ips.delete(floating_ip_id)
|
|
|
|
|
|
def snapshot_create(request, instance_id, name):
|
|
return novaclient(request).servers.create_image(instance_id, name)
|
|
|
|
|
|
def keypair_create(request, name):
|
|
return novaclient(request).keypairs.create(name)
|
|
|
|
|
|
def keypair_import(request, name, public_key):
|
|
return novaclient(request).keypairs.create(name, public_key)
|
|
|
|
|
|
def keypair_delete(request, keypair_id):
|
|
novaclient(request).keypairs.delete(keypair_id)
|
|
|
|
|
|
def keypair_list(request):
|
|
return novaclient(request).keypairs.list()
|
|
|
|
|
|
def server_create(request, name, image, flavor, key_name, user_data,
|
|
security_groups, block_device_mapping, instance_count=1):
|
|
return Server(novaclient(request).servers.create(
|
|
name, image, flavor, userdata=user_data,
|
|
security_groups=security_groups,
|
|
key_name=key_name, block_device_mapping=block_device_mapping,
|
|
min_count=instance_count), request)
|
|
|
|
|
|
def server_delete(request, instance):
|
|
novaclient(request).servers.delete(instance)
|
|
|
|
|
|
def server_get(request, instance_id):
|
|
return Server(novaclient(request).servers.get(instance_id), request)
|
|
|
|
|
|
def server_list(request, search_opts=None, all_tenants=False):
|
|
if search_opts is None:
|
|
search_opts = {}
|
|
if all_tenants:
|
|
search_opts['all_tenants'] = True
|
|
else:
|
|
search_opts['project_id'] = request.user.tenant_id
|
|
return [Server(s, request) for s in novaclient(request).\
|
|
servers.list(True, search_opts)]
|
|
|
|
|
|
def server_console_output(request, instance_id, tail_length=None):
|
|
"""Gets console output of an instance."""
|
|
return novaclient(request).servers.get_console_output(instance_id,
|
|
length=tail_length)
|
|
|
|
|
|
def server_pause(request, instance_id):
|
|
novaclient(request).servers.pause(instance_id)
|
|
|
|
|
|
def server_unpause(request, instance_id):
|
|
novaclient(request).servers.unpause(instance_id)
|
|
|
|
|
|
def server_suspend(request, instance_id):
|
|
novaclient(request).servers.suspend(instance_id)
|
|
|
|
|
|
def server_resume(request, instance_id):
|
|
novaclient(request).servers.resume(instance_id)
|
|
|
|
|
|
def server_reboot(request, instance_id, hardness=REBOOT_HARD):
|
|
server = server_get(request, instance_id)
|
|
server.reboot(hardness)
|
|
|
|
|
|
def server_update(request, instance_id, name):
|
|
return novaclient(request).servers.update(instance_id, name=name)
|
|
|
|
|
|
def server_add_floating_ip(request, server, floating_ip):
|
|
"""Associates floating IP to server's fixed IP.
|
|
"""
|
|
server = novaclient(request).servers.get(server)
|
|
fip = novaclient(request).floating_ips.get(floating_ip)
|
|
return novaclient(request).servers.add_floating_ip(server.id, fip.ip)
|
|
|
|
|
|
def server_remove_floating_ip(request, server, floating_ip):
|
|
"""Removes relationship between floating and server's fixed ip.
|
|
"""
|
|
fip = novaclient(request).floating_ips.get(floating_ip)
|
|
server = novaclient(request).servers.get(fip.instance_id)
|
|
return novaclient(request).servers.remove_floating_ip(server.id, fip.ip)
|
|
|
|
|
|
def tenant_quota_get(request, tenant_id):
|
|
return QuotaSet(novaclient(request).quotas.get(tenant_id))
|
|
|
|
|
|
def tenant_quota_update(request, tenant_id, **kwargs):
|
|
novaclient(request).quotas.update(tenant_id, **kwargs)
|
|
|
|
|
|
def tenant_quota_defaults(request, tenant_id):
|
|
return QuotaSet(novaclient(request).quotas.defaults(tenant_id))
|
|
|
|
|
|
def usage_get(request, tenant_id, start, end):
|
|
return Usage(novaclient(request).usage.get(tenant_id, start, end))
|
|
|
|
|
|
def usage_list(request, start, end):
|
|
return [Usage(u) for u in novaclient(request).usage.list(start, end, True)]
|
|
|
|
|
|
def tenant_quota_usages(request):
|
|
"""Builds a dictionary of current usage against quota for the current
|
|
tenant.
|
|
"""
|
|
# TODO(tres): Make this capture floating_ips and volumes as well.
|
|
instances = server_list(request)
|
|
quotas = tenant_quota_get(request, request.user.tenant_id)
|
|
flavors = dict([(f.id, f) for f in flavor_list(request)])
|
|
usages = {'instances': {'flavor_fields': [], 'used': len(instances)},
|
|
'cores': {'flavor_fields': ['vcpus'], 'used': 0},
|
|
'gigabytes': {'flavor_fields': ['disk', 'ephemeral'], 'used': 0},
|
|
'ram': {'flavor_fields': ['ram'], 'used': 0}}
|
|
|
|
for usage in usages:
|
|
for instance in instances:
|
|
for flavor_field in usages[usage]['flavor_fields']:
|
|
usages[usage]['used'] += getattr(
|
|
flavors[instance.flavor['id']], flavor_field)
|
|
usages[usage]['quota'] = getattr(quotas, usage)
|
|
usages[usage]['available'] = usages[usage]['quota'] - \
|
|
usages[usage]['used']
|
|
|
|
return usages
|
|
|
|
|
|
def security_group_list(request):
|
|
return [SecurityGroup(g) for g in novaclient(request).\
|
|
security_groups.list()]
|
|
|
|
|
|
def security_group_get(request, security_group_id):
|
|
return SecurityGroup(novaclient(request).\
|
|
security_groups.get(security_group_id))
|
|
|
|
|
|
def security_group_create(request, name, description):
|
|
return SecurityGroup(novaclient(request).\
|
|
security_groups.create(name, description))
|
|
|
|
|
|
def security_group_delete(request, security_group_id):
|
|
novaclient(request).security_groups.delete(security_group_id)
|
|
|
|
|
|
def security_group_rule_create(request, parent_group_id, ip_protocol=None,
|
|
from_port=None, to_port=None, cidr=None,
|
|
group_id=None):
|
|
return SecurityGroupRule(novaclient(request).\
|
|
security_group_rules.create(parent_group_id,
|
|
ip_protocol,
|
|
from_port,
|
|
to_port,
|
|
cidr,
|
|
group_id))
|
|
|
|
|
|
def security_group_rule_delete(request, security_group_rule_id):
|
|
novaclient(request).security_group_rules.delete(security_group_rule_id)
|
|
|
|
|
|
def virtual_interfaces_list(request, instance_id):
|
|
return novaclient(request).virtual_interfaces.list(instance_id)
|
|
|
|
|
|
def volume_list(request):
|
|
return cinderclient(request).volumes.list()
|
|
|
|
|
|
def volume_get(request, volume_id):
|
|
return cinderclient(request).volumes.get(volume_id)
|
|
|
|
|
|
def volume_instance_list(request, instance_id):
|
|
return novaclient(request).volumes.get_server_volumes(instance_id)
|
|
|
|
|
|
def volume_create(request, size, name, description):
|
|
return cinderclient(request).volumes.create(size, display_name=name,
|
|
display_description=description)
|
|
|
|
|
|
def volume_delete(request, volume_id):
|
|
cinderclient(request).volumes.delete(volume_id)
|
|
|
|
|
|
def volume_attach(request, volume_id, instance_id, device):
|
|
novaclient(request).volumes.create_server_volume(instance_id,
|
|
volume_id,
|
|
device)
|
|
|
|
|
|
def volume_detach(request, instance_id, attachment_id):
|
|
novaclient(request).volumes.delete_server_volume(
|
|
instance_id, attachment_id)
|
|
|
|
|
|
def volume_snapshot_list(request):
|
|
return cinderclient(request).volume_snapshots.list()
|
|
|
|
|
|
def volume_snapshot_create(request, volume_id, name, description):
|
|
return cinderclient(request).volume_snapshots.create(
|
|
volume_id, display_name=name, display_description=description)
|
|
|
|
|
|
def volume_snapshot_delete(request, snapshot_id):
|
|
cinderclient(request).volume_snapshots.delete(snapshot_id)
|
|
|
|
|
|
def get_x509_credentials(request):
|
|
return novaclient(request).certs.create()
|
|
|
|
|
|
def get_x509_root_certificate(request):
|
|
return novaclient(request).certs.get()
|