989 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			989 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
						|
 | 
						|
# Copyright 2011 United States Government as represented by the
 | 
						|
# Administrator of the National Aeronautics and Space Administration.
 | 
						|
# All Rights Reserved.
 | 
						|
#
 | 
						|
# Copyright 2011 Nebula, 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.
 | 
						|
 | 
						|
"""
 | 
						|
Methods and interface objects used to interact with external apis.
 | 
						|
 | 
						|
API method calls return objects that are in many cases objects with
 | 
						|
attributes that are direct maps to the data returned from the API http call.
 | 
						|
Unfortunately, these objects are also often constructed dynamically, making
 | 
						|
it difficult to know what data is available from the API object.  Because of
 | 
						|
this, all API calls should wrap their returned object in one defined here,
 | 
						|
using only explicitly defined atributes and/or methods.
 | 
						|
 | 
						|
In other words, django_openstack developers not working on django_openstack.api
 | 
						|
shouldn't need to understand the finer details of APIs for Nova/Glance/Swift et
 | 
						|
al.
 | 
						|
"""
 | 
						|
 | 
						|
from django.conf import settings
 | 
						|
from django.contrib import messages
 | 
						|
 | 
						|
import cloudfiles
 | 
						|
import glance.client
 | 
						|
import glance.common.exception as glance_exceptions
 | 
						|
import httplib
 | 
						|
import json
 | 
						|
import logging
 | 
						|
import openstack.compute
 | 
						|
import openstackx.admin
 | 
						|
import openstackx.api.exceptions as api_exceptions
 | 
						|
import openstackx.extras
 | 
						|
import openstackx.auth
 | 
						|
from novaclient.v1_1 import client
 | 
						|
import quantum.client
 | 
						|
from urlparse import urlparse
 | 
						|
 | 
						|
LOG = logging.getLogger('django_openstack.api')
 | 
						|
 | 
						|
 | 
						|
class APIResourceWrapper(object):
 | 
						|
    """ Simple wrapper for api objects
 | 
						|
 | 
						|
        Define _attrs on the child class and pass in the
 | 
						|
        api object as the only argument to the constructor
 | 
						|
    """
 | 
						|
    _attrs = []
 | 
						|
 | 
						|
    def __init__(self, apiresource):
 | 
						|
        self._apiresource = apiresource
 | 
						|
 | 
						|
    def __getattr__(self, attr):
 | 
						|
        if attr in self._attrs:
 | 
						|
            # __getattr__ won't find properties
 | 
						|
            return self._apiresource.__getattribute__(attr)
 | 
						|
        else:
 | 
						|
            LOG.debug('Attempted to access unknown attribute "%s" on'
 | 
						|
                      ' APIResource object of type "%s" wrapping resource of'
 | 
						|
                      ' type "%s"' % (attr, self.__class__,
 | 
						|
                                      self._apiresource.__class__))
 | 
						|
            raise AttributeError(attr)
 | 
						|
 | 
						|
 | 
						|
class APIDictWrapper(object):
 | 
						|
    """ Simple wrapper for api dictionaries
 | 
						|
 | 
						|
        Some api calls return dictionaries.  This class provides identical
 | 
						|
        behavior as APIResourceWrapper, except that it will also behave as a
 | 
						|
        dictionary, in addition to attribute accesses.
 | 
						|
 | 
						|
        Attribute access is the preferred method of access, to be
 | 
						|
        consistent with api resource objects from openstackx
 | 
						|
    """
 | 
						|
    def __init__(self, apidict):
 | 
						|
        self._apidict = apidict
 | 
						|
 | 
						|
    def __getattr__(self, attr):
 | 
						|
        if attr in self._attrs:
 | 
						|
            try:
 | 
						|
                return self._apidict[attr]
 | 
						|
            except KeyError, e:
 | 
						|
                raise AttributeError(e)
 | 
						|
 | 
						|
        else:
 | 
						|
            LOG.debug('Attempted to access unknown item "%s" on'
 | 
						|
                      'APIResource object of type "%s"'
 | 
						|
                      % (attr, self.__class__))
 | 
						|
            raise AttributeError(attr)
 | 
						|
 | 
						|
    def __getitem__(self, item):
 | 
						|
        try:
 | 
						|
            return self.__getattr__(item)
 | 
						|
        except AttributeError, e:
 | 
						|
            # caller is expecting a KeyError
 | 
						|
            raise KeyError(e)
 | 
						|
 | 
						|
    def get(self, item, default=None):
 | 
						|
        try:
 | 
						|
            return self.__getattr__(item)
 | 
						|
        except AttributeError:
 | 
						|
            return default
 | 
						|
 | 
						|
 | 
						|
class Container(APIResourceWrapper):
 | 
						|
    """Simple wrapper around cloudfiles.container.Container"""
 | 
						|
    _attrs = ['name']
 | 
						|
 | 
						|
 | 
						|
class Console(APIResourceWrapper):
 | 
						|
    """Simple wrapper around openstackx.extras.consoles.Console"""
 | 
						|
    _attrs = ['id', 'output', 'type']
 | 
						|
 | 
						|
 | 
						|
class Flavor(APIResourceWrapper):
 | 
						|
    """Simple wrapper around openstackx.admin.flavors.Flavor"""
 | 
						|
    _attrs = ['disk', 'id', 'links', 'name', 'ram', 'vcpus']
 | 
						|
 | 
						|
 | 
						|
class FloatingIp(APIResourceWrapper):
 | 
						|
    """Simple wrapper for floating ips"""
 | 
						|
    _attrs = ['ip', 'fixed_ip', 'instance_id', 'id']
 | 
						|
 | 
						|
 | 
						|
class Image(APIDictWrapper):
 | 
						|
    """Simple wrapper around glance image dictionary"""
 | 
						|
    _attrs = ['checksum', 'container_format', 'created_at', 'deleted',
 | 
						|
             'deleted_at', 'disk_format', 'id', 'is_public', 'location',
 | 
						|
             'name', 'properties', 'size', 'status', 'updated_at']
 | 
						|
 | 
						|
    def __getattr__(self, attrname):
 | 
						|
        if attrname == "properties":
 | 
						|
            return ImageProperties(super(Image, self).__getattr__(attrname))
 | 
						|
        else:
 | 
						|
            return super(Image, self).__getattr__(attrname)
 | 
						|
 | 
						|
 | 
						|
class ImageProperties(APIDictWrapper):
 | 
						|
    """Simple wrapper around glance image properties dictionary"""
 | 
						|
    _attrs = ['architecture', 'image_location', 'image_state', 'kernel_id',
 | 
						|
             'project_id', 'ramdisk_id']
 | 
						|
 | 
						|
 | 
						|
class KeyPair(APIResourceWrapper):
 | 
						|
    """Simple wrapper around openstackx.extras.keypairs.Keypair"""
 | 
						|
    _attrs = ['fingerprint', 'name', 'private_key']
 | 
						|
 | 
						|
 | 
						|
class Server(APIResourceWrapper):
 | 
						|
    """Simple wrapper around openstackx.extras.server.Server
 | 
						|
 | 
						|
       Preserves the request info so image name can later be retrieved
 | 
						|
    """
 | 
						|
    _attrs = ['addresses', 'attrs', 'hostId', 'id', 'image', 'links',
 | 
						|
             'metadata', 'name', 'private_ip', 'public_ip', 'status', 'uuid',
 | 
						|
             'image_name', 'VirtualInterfaces']
 | 
						|
 | 
						|
    def __init__(self, apiresource, request):
 | 
						|
        super(Server, self).__init__(apiresource)
 | 
						|
        self.request = request
 | 
						|
 | 
						|
    def __getattr__(self, attr):
 | 
						|
        if attr == "attrs":
 | 
						|
            return ServerAttributes(super(Server, self).__getattr__(attr))
 | 
						|
        else:
 | 
						|
            return super(Server, self).__getattr__(attr)
 | 
						|
 | 
						|
    @property
 | 
						|
    def image_name(self):
 | 
						|
        try:
 | 
						|
            image = image_get(self.request, self.image['id'])
 | 
						|
            return image.name
 | 
						|
        except glance_exceptions.NotFound:
 | 
						|
            return "(not found)"
 | 
						|
 | 
						|
    def reboot(self, hardness=openstack.compute.servers.REBOOT_HARD):
 | 
						|
        compute_api(self.request).servers.reboot(self.id, hardness)
 | 
						|
 | 
						|
 | 
						|
class ServerAttributes(APIDictWrapper):
 | 
						|
    """Simple wrapper around openstackx.extras.server.Server attributes
 | 
						|
 | 
						|
       Preserves the request info so image name can later be retrieved
 | 
						|
    """
 | 
						|
    _attrs = ['description', 'disk_gb', 'host', 'image_ref', 'kernel_id',
 | 
						|
              'key_name', 'launched_at', 'mac_address', 'memory_mb', 'name',
 | 
						|
              'os_type', 'tenant_id', 'ramdisk_id', 'scheduled_at',
 | 
						|
              'terminated_at', 'user_data', 'user_id', 'vcpus', 'hostname',
 | 
						|
              'security_groups']
 | 
						|
 | 
						|
 | 
						|
class Services(APIResourceWrapper):
 | 
						|
    _attrs = ['disabled', 'host', 'id', 'last_update', 'stats', 'type', 'up',
 | 
						|
             'zone']
 | 
						|
 | 
						|
 | 
						|
class SwiftObject(APIResourceWrapper):
 | 
						|
    _attrs = ['name']
 | 
						|
 | 
						|
 | 
						|
class Tenant(APIResourceWrapper):
 | 
						|
    """Simple wrapper around openstackx.auth.tokens.Tenant"""
 | 
						|
    _attrs = ['id', 'description', 'enabled', 'name']
 | 
						|
 | 
						|
 | 
						|
class Token(APIResourceWrapper):
 | 
						|
    """Simple wrapper around openstackx.auth.tokens.Token"""
 | 
						|
    _attrs = ['id', 'serviceCatalog', 'tenant_id', 'user']
 | 
						|
 | 
						|
 | 
						|
class Usage(APIResourceWrapper):
 | 
						|
    """Simple wrapper around openstackx.extras.usage.Usage"""
 | 
						|
    _attrs = ['begin', 'instances', 'stop', 'tenant_id',
 | 
						|
             'total_active_disk_size', 'total_active_instances',
 | 
						|
             'total_active_ram_size', 'total_active_vcpus', 'total_cpu_usage',
 | 
						|
             'total_disk_usage', 'total_hours', 'total_ram_usage']
 | 
						|
 | 
						|
 | 
						|
class User(APIResourceWrapper):
 | 
						|
    """Simple wrapper around openstackx.extras.users.User"""
 | 
						|
    _attrs = ['email', 'enabled', 'id', 'tenantId', 'name']
 | 
						|
 | 
						|
 | 
						|
class Role(APIResourceWrapper):
 | 
						|
    """Wrapper around user role"""
 | 
						|
    _attrs = ['id', 'name', 'description', 'service_id']
 | 
						|
 | 
						|
 | 
						|
class SecurityGroup(APIResourceWrapper):
 | 
						|
    """Simple wrapper around openstackx.extras.security_groups.SecurityGroup"""
 | 
						|
    _attrs = ['id', 'name', 'description', 'tenant_id', 'rules']
 | 
						|
 | 
						|
 | 
						|
class SecurityGroupRule(APIResourceWrapper):
 | 
						|
    """Simple wrapper around openstackx.extras.security_groups.SecurityGroupRule"""
 | 
						|
    _attrs = ['id', 'parent_group_id', 'group_id', 'ip_protocol',
 | 
						|
              'from_port', 'to_port', 'groups', 'ip_ranges']
 | 
						|
 | 
						|
 | 
						|
class SecurityGroupRule(APIResourceWrapper):
 | 
						|
    """Simple wrapper around openstackx.extras.users.User"""
 | 
						|
    _attrs = ['id', 'name', 'description', 'tenant_id', 'security_group_rules']
 | 
						|
 | 
						|
 | 
						|
class SwiftAuthentication(object):
 | 
						|
    """Auth container to pass CloudFiles storage URL and token from
 | 
						|
       session.
 | 
						|
    """
 | 
						|
    def __init__(self, storage_url, auth_token):
 | 
						|
        self.storage_url = storage_url
 | 
						|
        self.auth_token = auth_token
 | 
						|
 | 
						|
    def authenticate(self):
 | 
						|
        return (self.storage_url, '', self.auth_token)
 | 
						|
 | 
						|
 | 
						|
class ServiceCatalogException(api_exceptions.ApiException):
 | 
						|
    def __init__(self, service_name):
 | 
						|
        message = 'Invalid service catalog service: %s' % service_name
 | 
						|
        super(ServiceCatalogException, self).__init__(404, message)
 | 
						|
 | 
						|
 | 
						|
class VirtualInterface(APIResourceWrapper):
 | 
						|
    _attrs = ['id', 'mac_address']
 | 
						|
 | 
						|
 | 
						|
def get_service_from_catalog(catalog, service_type):
 | 
						|
    for service in catalog:
 | 
						|
        if service['type'] == service_type:
 | 
						|
            return service
 | 
						|
    return None
 | 
						|
 | 
						|
 | 
						|
def url_for(request, service_type, admin=False):
 | 
						|
    catalog = request.user.service_catalog
 | 
						|
    service = get_service_from_catalog(catalog, service_type)
 | 
						|
    if service:
 | 
						|
        try:
 | 
						|
            if admin:
 | 
						|
                return service['endpoints'][0]['adminURL']
 | 
						|
            else:
 | 
						|
                return service['endpoints'][0]['internalURL']
 | 
						|
        except (IndexError, KeyError):
 | 
						|
            raise ServiceCatalogException(service_type)
 | 
						|
    else:
 | 
						|
        raise ServiceCatalogException(service_type)
 | 
						|
 | 
						|
 | 
						|
def check_openstackx(f):
 | 
						|
    """Decorator that adds extra info to api exceptions
 | 
						|
 | 
						|
       The dashboard currently depends on openstackx extensions being present
 | 
						|
       in nova.  Error messages depending for views depending on these
 | 
						|
       extensions do not lead to the conclusion that nova is missing
 | 
						|
       extensions.
 | 
						|
 | 
						|
       This decorator should be dropped and removed after keystone and
 | 
						|
       dashboard more gracefully handle extensions and openstackx extensions
 | 
						|
       aren't required by the dashboard in nova.
 | 
						|
    """
 | 
						|
    def inner(*args, **kwargs):
 | 
						|
        try:
 | 
						|
            return f(*args, **kwargs)
 | 
						|
        except api_exceptions.NotFound, e:
 | 
						|
            e.message = e.details or ''
 | 
						|
            e.message += ' This error may be caused by a misconfigured' \
 | 
						|
                         ' nova url in keystone\'s service catalog, or ' \
 | 
						|
                         ' by missing openstackx extensions in nova. ' \
 | 
						|
                         ' See the dashboard README.'
 | 
						|
            raise
 | 
						|
 | 
						|
    return inner
 | 
						|
 | 
						|
 | 
						|
def compute_api(request):
 | 
						|
    compute = openstack.compute.Compute(
 | 
						|
        auth_token=request.user.token,
 | 
						|
        management_url=url_for(request, 'compute'))
 | 
						|
    # this below hack is necessary to make the jacobian compute client work
 | 
						|
    # TODO(mgius): It looks like this is unused now?
 | 
						|
    compute.client.auth_token = request.user.token
 | 
						|
    compute.client.management_url = url_for(request, 'compute')
 | 
						|
    LOG.debug('compute_api connection created using token "%s"'
 | 
						|
                      ' and url "%s"' %
 | 
						|
                      (request.user.token, url_for(request, 'compute')))
 | 
						|
    return compute
 | 
						|
 | 
						|
 | 
						|
def account_api(request):
 | 
						|
    LOG.debug('account_api connection created using token "%s"'
 | 
						|
                      ' and url "%s"' %
 | 
						|
                  (request.user.token,
 | 
						|
                   url_for(request, 'identity', True)))
 | 
						|
    return openstackx.extras.Account(
 | 
						|
        auth_token=request.user.token,
 | 
						|
        management_url=url_for(request, 'identity', True))
 | 
						|
 | 
						|
 | 
						|
def glance_api(request):
 | 
						|
    o = urlparse(url_for(request, 'image'))
 | 
						|
    LOG.debug('glance_api connection created for host "%s:%d"' %
 | 
						|
                     (o.hostname, o.port))
 | 
						|
    return glance.client.Client(o.hostname, o.port, auth_tok=request.user.token)
 | 
						|
 | 
						|
 | 
						|
def admin_api(request):
 | 
						|
    LOG.debug('admin_api connection created using token "%s"'
 | 
						|
                    ' and url "%s"' %
 | 
						|
                    (request.user.token, url_for(request, 'compute', True)))
 | 
						|
    return openstackx.admin.Admin(auth_token=request.user.token,
 | 
						|
                                 management_url=url_for(request, 'compute', True))
 | 
						|
 | 
						|
 | 
						|
def extras_api(request):
 | 
						|
    LOG.debug('extras_api connection created using token "%s"'
 | 
						|
                     ' and url "%s"' %
 | 
						|
                    (request.user.token, url_for(request, 'compute')))
 | 
						|
    return openstackx.extras.Extras(auth_token=request.user.token,
 | 
						|
                                   management_url=url_for(request, 'compute'))
 | 
						|
 | 
						|
 | 
						|
def novaclient(request):
 | 
						|
    LOG.debug('novaclient connection created using token "%s"'
 | 
						|
              ' and url "%s"' % (request.user.token, url_for(request, 'compute')))
 | 
						|
    c = client.Client(username=request.user.username,
 | 
						|
                      api_key=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 auth_api():
 | 
						|
    LOG.debug('auth_api connection created using url "%s"' %
 | 
						|
                   settings.OPENSTACK_KEYSTONE_URL)
 | 
						|
    return openstackx.auth.Auth(
 | 
						|
            management_url=settings.OPENSTACK_KEYSTONE_URL)
 | 
						|
 | 
						|
 | 
						|
def swift_api(request):
 | 
						|
    LOG.debug('object store connection created using token "%s"'
 | 
						|
                ' and url "%s"' %
 | 
						|
                (request.session['token'], url_for(request, 'swift')))
 | 
						|
    auth = SwiftAuthentication(url_for(request, 'swift'),
 | 
						|
                               request.session['token'])
 | 
						|
    return cloudfiles.get_connection(auth=auth)
 | 
						|
 | 
						|
 | 
						|
def quantum_api(request):
 | 
						|
    tenant = None
 | 
						|
    if hasattr(request, 'user'):
 | 
						|
        tenant = request.user.tenant_id
 | 
						|
    else:
 | 
						|
        tenant = settings.QUANTUM_TENANT
 | 
						|
 | 
						|
    return quantum.client.Client(settings.QUANTUM_URL, settings.QUANTUM_PORT,
 | 
						|
                  False, tenant, 'json')
 | 
						|
 | 
						|
 | 
						|
def console_create(request, instance_id, kind='text'):
 | 
						|
    return Console(extras_api(request).consoles.create(instance_id, kind))
 | 
						|
 | 
						|
 | 
						|
def flavor_create(request, name, memory, vcpu, disk, flavor_id):
 | 
						|
    # TODO -- convert to novaclient when novaclient adds create support
 | 
						|
    return Flavor(admin_api(request).flavors.create(
 | 
						|
            name, int(memory), int(vcpu), int(disk), flavor_id))
 | 
						|
 | 
						|
 | 
						|
def flavor_delete(request, flavor_id, purge=False):
 | 
						|
    # TODO -- convert to novaclient when novaclient adds delete support
 | 
						|
    admin_api(request).flavors.delete(flavor_id, purge)
 | 
						|
 | 
						|
 | 
						|
def flavor_get(request, flavor_id):
 | 
						|
    return Flavor(novaclient(request).flavors.get(flavor_id))
 | 
						|
 | 
						|
 | 
						|
def flavor_list(request):
 | 
						|
    return [Flavor(f) for f in novaclient(request).flavors.list()]
 | 
						|
 | 
						|
 | 
						|
def tenant_floating_ip_list(request):
 | 
						|
    """
 | 
						|
    Fetches a list of all floating ips.
 | 
						|
    """
 | 
						|
    return [FloatingIp(ip) for ip in novaclient(request).floating_ips.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):
 | 
						|
    """
 | 
						|
    Allocates a floating ip to tenant.
 | 
						|
    """
 | 
						|
    return novaclient(request).floating_ips.create()
 | 
						|
 | 
						|
 | 
						|
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 image_create(request, image_meta, image_file):
 | 
						|
    return Image(glance_api(request).add_image(image_meta, image_file))
 | 
						|
 | 
						|
 | 
						|
def image_delete(request, image_id):
 | 
						|
    return glance_api(request).delete_image(image_id)
 | 
						|
 | 
						|
 | 
						|
def image_get(request, image_id):
 | 
						|
    return Image(glance_api(request).get_image(image_id)[0])
 | 
						|
 | 
						|
 | 
						|
def image_list_detailed(request):
 | 
						|
    return [Image(i) for i in glance_api(request).get_images_detailed()]
 | 
						|
 | 
						|
 | 
						|
def snapshot_list_detailed(request):
 | 
						|
    filters = {}
 | 
						|
    filters['property-image_type'] = 'snapshot'
 | 
						|
    filters['is_public'] = 'none'
 | 
						|
    return [Image(i) for i in glance_api(request)
 | 
						|
                             .get_images_detailed(filters=filters)]
 | 
						|
 | 
						|
 | 
						|
def snapshot_create(request, instance_id, name):
 | 
						|
    return novaclient(request).servers.create_image(instance_id, name)
 | 
						|
 | 
						|
 | 
						|
def image_update(request, image_id, image_meta=None):
 | 
						|
    image_meta = image_meta and image_meta or {}
 | 
						|
    return Image(glance_api(request).update_image(image_id,
 | 
						|
                                                  image_meta=image_meta))
 | 
						|
 | 
						|
 | 
						|
def keypair_create(request, name):
 | 
						|
    return KeyPair(novaclient(request).keypairs.create(name))
 | 
						|
 | 
						|
 | 
						|
def keypair_delete(request, keypair_id):
 | 
						|
    novaclient(request).keypairs.delete(keypair_id)
 | 
						|
 | 
						|
 | 
						|
def keypair_list(request):
 | 
						|
    return [KeyPair(key) for key in novaclient(request).keypairs.list()]
 | 
						|
 | 
						|
 | 
						|
def server_create(request, name, image, flavor,
 | 
						|
                           key_name, user_data, security_groups):
 | 
						|
    return Server(novaclient(request).servers.create(
 | 
						|
            name, image, flavor, userdata=user_data,
 | 
						|
            security_groups=security_groups,
 | 
						|
            key_name=key_name), request)
 | 
						|
 | 
						|
 | 
						|
def server_delete(request, instance):
 | 
						|
    compute_api(request).servers.delete(instance)
 | 
						|
 | 
						|
 | 
						|
def server_get(request, instance_id):
 | 
						|
    return Server(extras_api(request).servers.get(instance_id), request)
 | 
						|
 | 
						|
 | 
						|
@check_openstackx
 | 
						|
def server_list(request):
 | 
						|
    return [Server(s, request) for s in extras_api(request).servers.list()]
 | 
						|
 | 
						|
 | 
						|
@check_openstackx
 | 
						|
def admin_server_list(request):
 | 
						|
    return [Server(s, request) for s in admin_api(request).servers.list()]
 | 
						|
 | 
						|
 | 
						|
def server_reboot(request,
 | 
						|
                  instance_id,
 | 
						|
                  hardness=openstack.compute.servers.REBOOT_HARD):
 | 
						|
    server = server_get(request, instance_id)
 | 
						|
    server.reboot(hardness)
 | 
						|
 | 
						|
 | 
						|
def server_update(request, instance_id, name, description):
 | 
						|
    return extras_api(request).servers.update(instance_id,
 | 
						|
                                              name=name,
 | 
						|
                                              description=description)
 | 
						|
 | 
						|
 | 
						|
def server_add_floating_ip(request, server, address):
 | 
						|
    """
 | 
						|
    Associates floating IP to server's fixed IP.
 | 
						|
    """
 | 
						|
    server = novaclient(request).servers.get(server)
 | 
						|
    fip = novaclient(request).floating_ips.get(address)
 | 
						|
 | 
						|
    return novaclient(request).servers.add_floating_ip(server, fip)
 | 
						|
 | 
						|
 | 
						|
def server_remove_floating_ip(request, server, address):
 | 
						|
    """
 | 
						|
    Removes relationship between floating and server's fixed ip.
 | 
						|
    """
 | 
						|
    fip = novaclient(request).floating_ips.get(address)
 | 
						|
    server = novaclient(request).servers.get(fip.instance_id)
 | 
						|
 | 
						|
    return novaclient(request).servers.remove_floating_ip(server, fip)
 | 
						|
 | 
						|
 | 
						|
def service_get(request, name):
 | 
						|
    return Services(admin_api(request).services.get(name))
 | 
						|
 | 
						|
 | 
						|
@check_openstackx
 | 
						|
def service_list(request):
 | 
						|
    return [Services(s) for s in admin_api(request).services.list()]
 | 
						|
 | 
						|
 | 
						|
def service_update(request, name, enabled):
 | 
						|
    return Services(admin_api(request).services.update(name, enabled))
 | 
						|
 | 
						|
 | 
						|
def token_get_tenant(request, tenant_id):
 | 
						|
    tenants = auth_api().tenants.for_token(request.user.token)
 | 
						|
    for t in tenants:
 | 
						|
        if str(t.id) == str(tenant_id):
 | 
						|
            return Tenant(t)
 | 
						|
 | 
						|
    LOG.warning('Unknown tenant id "%s" requested' % tenant_id)
 | 
						|
 | 
						|
 | 
						|
def token_list_tenants(request, token):
 | 
						|
    return [Tenant(t) for t in auth_api().tenants.for_token(token)]
 | 
						|
 | 
						|
 | 
						|
def tenant_create(request, name, description, enabled):
 | 
						|
    return Tenant(account_api(request).tenants.create(name,
 | 
						|
                                                      description,
 | 
						|
                                                      enabled))
 | 
						|
 | 
						|
 | 
						|
def tenant_get(request, tenant_id):
 | 
						|
    return Tenant(account_api(request).tenants.get(tenant_id))
 | 
						|
 | 
						|
 | 
						|
@check_openstackx
 | 
						|
def tenant_list(request):
 | 
						|
    return [Tenant(t) for t in account_api(request).tenants.list()]
 | 
						|
 | 
						|
 | 
						|
def tenant_list_for_token(request, token):
 | 
						|
    # FIXME: use novaclient for this
 | 
						|
    keystone =  openstackx.auth.Auth(
 | 
						|
            management_url=settings.OPENSTACK_KEYSTONE_URL)
 | 
						|
    return [Tenant(t) for t in keystone.tenants.for_token(token)]
 | 
						|
 | 
						|
 | 
						|
def users_list_for_token_and_tenant(request, token, tenant):
 | 
						|
    admin_account =  openstackx.extras.Account(
 | 
						|
                     auth_token=token,
 | 
						|
                     management_url=settings.OPENSTACK_KEYSTONE_ADMIN_URL)
 | 
						|
    return [User(u) for u in admin_account.users.get_for_tenant(tenant)]
 | 
						|
 | 
						|
 | 
						|
def tenant_update(request, tenant_id, description, enabled):
 | 
						|
    return Tenant(account_api(request).tenants.update(tenant_id,
 | 
						|
                                                      description,
 | 
						|
                                                      enabled))
 | 
						|
 | 
						|
 | 
						|
def token_create(request, tenant, username, password):
 | 
						|
    return Token(auth_api().tokens.create(tenant, username, password))
 | 
						|
 | 
						|
 | 
						|
def token_create_scoped_with_token(request, tenant, token):
 | 
						|
    return Token(auth_api().tokens.create_scoped_with_token(tenant, token))
 | 
						|
 | 
						|
 | 
						|
def tenant_quota_get(request, tenant):
 | 
						|
    return novaclient(request).quotas.get(tenant)
 | 
						|
 | 
						|
 | 
						|
@check_openstackx
 | 
						|
def usage_get(request, tenant_id, start, end):
 | 
						|
    return Usage(extras_api(request).usage.get(tenant_id, start, end))
 | 
						|
 | 
						|
 | 
						|
@check_openstackx
 | 
						|
def usage_list(request, start, end):
 | 
						|
    return [Usage(u) for u in extras_api(request).usage.list(start, end)]
 | 
						|
 | 
						|
 | 
						|
def user_create(request, user_id, email, password, tenant_id, enabled):
 | 
						|
    return User(account_api(request).users.create(
 | 
						|
            user_id, email, password, tenant_id, enabled))
 | 
						|
 | 
						|
 | 
						|
def user_delete(request, user_id):
 | 
						|
    account_api(request).users.delete(user_id)
 | 
						|
 | 
						|
 | 
						|
def user_get(request, user_id):
 | 
						|
    return User(account_api(request).users.get(user_id))
 | 
						|
 | 
						|
 | 
						|
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 SecurityGroup(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)
 | 
						|
 | 
						|
 | 
						|
@check_openstackx
 | 
						|
def user_list(request):
 | 
						|
    return [User(u) for u in account_api(request).users.list()]
 | 
						|
 | 
						|
 | 
						|
def user_update_email(request, user_id, email):
 | 
						|
    return User(account_api(request).users.update_email(user_id, email))
 | 
						|
 | 
						|
 | 
						|
def user_update_enabled(request, user_id, enabled):
 | 
						|
    return User(account_api(request).users.update_enabled(user_id, enabled))
 | 
						|
 | 
						|
 | 
						|
def user_update_password(request, user_id, password):
 | 
						|
    return User(account_api(request).users.update_password(user_id, password))
 | 
						|
 | 
						|
 | 
						|
def user_update_tenant(request, user_id, tenant_id):
 | 
						|
    return User(account_api(request).users.update_tenant(user_id, tenant_id))
 | 
						|
 | 
						|
 | 
						|
def _get_role(request, name):
 | 
						|
    roles = account_api(request).roles.list()
 | 
						|
    for role in roles:
 | 
						|
        if role.name.lower() == name.lower():
 | 
						|
           return role
 | 
						|
 | 
						|
    raise Exception('Role does not exist: %s' % name)
 | 
						|
 | 
						|
 | 
						|
def role_add_for_tenant_user(request, tenant_id, user_id, role_name):
 | 
						|
    role = _get_role(request, role_name)
 | 
						|
    account_api(request).role_refs.add_for_tenant_user(
 | 
						|
                tenant_id,
 | 
						|
                user_id,
 | 
						|
                role.id)
 | 
						|
 | 
						|
 | 
						|
def role_delete_for_tenant_user(request, tenant_id, user_id, role_name):
 | 
						|
    role = _get_role(request, role_name)
 | 
						|
    account_api(request).role_refs.delete_for_tenant_user(
 | 
						|
                tenant_id,
 | 
						|
                user_id,
 | 
						|
                role.id)
 | 
						|
 | 
						|
 | 
						|
def swift_container_exists(request, container_name):
 | 
						|
    try:
 | 
						|
        swift_api(request).get_container(container_name)
 | 
						|
        return True
 | 
						|
    except cloudfiles.errors.NoSuchContainer:
 | 
						|
        return False
 | 
						|
 | 
						|
 | 
						|
def swift_object_exists(request, container_name, object_name):
 | 
						|
    container = swift_api(request).get_container(container_name)
 | 
						|
 | 
						|
    try:
 | 
						|
        container.get_object(object_name)
 | 
						|
        return True
 | 
						|
    except cloudfiles.errors.NoSuchObject:
 | 
						|
        return False
 | 
						|
 | 
						|
 | 
						|
def swift_get_containers(request):
 | 
						|
    return [Container(c) for c in swift_api(request).get_all_containers()]
 | 
						|
 | 
						|
 | 
						|
def swift_create_container(request, name):
 | 
						|
    if swift_container_exists(request, name):
 | 
						|
        raise Exception('Container with name %s already exists.' % (name))
 | 
						|
 | 
						|
    return Container(swift_api(request).create_container(name))
 | 
						|
 | 
						|
 | 
						|
def swift_delete_container(request, name):
 | 
						|
    swift_api(request).delete_container(name)
 | 
						|
 | 
						|
 | 
						|
def swift_get_objects(request, container_name, prefix=None):
 | 
						|
    container = swift_api(request).get_container(container_name)
 | 
						|
    return [SwiftObject(o) for o in container.get_objects(prefix=prefix)]
 | 
						|
 | 
						|
 | 
						|
def swift_copy_object(request, orig_container_name, orig_object_name,
 | 
						|
                      new_container_name, new_object_name):
 | 
						|
 | 
						|
    container = swift_api(request).get_container(orig_container_name)
 | 
						|
 | 
						|
    if swift_object_exists(request,
 | 
						|
                           new_container_name,
 | 
						|
                           new_object_name) == True:
 | 
						|
        raise Exception('Object with name %s already exists in container %s'
 | 
						|
        % (new_object_name, new_container_name))
 | 
						|
 | 
						|
    orig_obj = container.get_object(orig_object_name)
 | 
						|
    return orig_obj.copy_to(new_container_name, new_object_name)
 | 
						|
 | 
						|
 | 
						|
def swift_upload_object(request, container_name, object_name, object_data):
 | 
						|
    container = swift_api(request).get_container(container_name)
 | 
						|
    obj = container.create_object(object_name)
 | 
						|
    obj.write(object_data)
 | 
						|
 | 
						|
 | 
						|
def swift_delete_object(request, container_name, object_name):
 | 
						|
    container = swift_api(request).get_container(container_name)
 | 
						|
    container.delete_object(object_name)
 | 
						|
 | 
						|
 | 
						|
def swift_get_object_data(request, container_name, object_name):
 | 
						|
    container = swift_api(request).get_container(container_name)
 | 
						|
    return container.get_object(object_name).stream()
 | 
						|
 | 
						|
 | 
						|
def quantum_list_networks(request):
 | 
						|
    return quantum_api(request).list_networks()
 | 
						|
 | 
						|
 | 
						|
def quantum_network_details(request, network_id):
 | 
						|
    return quantum_api(request).show_network_details(network_id)
 | 
						|
 | 
						|
 | 
						|
def quantum_list_ports(request, network_id):
 | 
						|
    return quantum_api(request).list_ports(network_id)
 | 
						|
 | 
						|
 | 
						|
def quantum_port_details(request, network_id, port_id):
 | 
						|
    return quantum_api(request).show_port_details(network_id, port_id)
 | 
						|
 | 
						|
 | 
						|
def quantum_create_network(request, data):
 | 
						|
    return quantum_api(request).create_network(data)
 | 
						|
 | 
						|
 | 
						|
def quantum_delete_network(request, network_id):
 | 
						|
    return quantum_api(request).delete_network(network_id)
 | 
						|
 | 
						|
 | 
						|
def quantum_update_network(request, network_id, data):
 | 
						|
    return quantum_api(request).update_network(network_id, data)
 | 
						|
 | 
						|
 | 
						|
def quantum_create_port(request, network_id):
 | 
						|
    return quantum_api(request).create_port(network_id)
 | 
						|
 | 
						|
 | 
						|
def quantum_delete_port(request, network_id, port_id):
 | 
						|
    return quantum_api(request).delete_port(network_id, port_id)
 | 
						|
 | 
						|
 | 
						|
def quantum_attach_port(request, network_id, port_id, data):
 | 
						|
    return quantum_api(request).attach_resource(network_id, port_id, data)
 | 
						|
 | 
						|
 | 
						|
def quantum_detach_port(request, network_id, port_id):
 | 
						|
    return quantum_api(request).detach_resource(network_id, port_id)
 | 
						|
 | 
						|
 | 
						|
def quantum_set_port_state(request, network_id, port_id, data):
 | 
						|
    return quantum_api(request).set_port_state(network_id, port_id, data)
 | 
						|
 | 
						|
 | 
						|
def quantum_port_attachment(request, network_id, port_id):
 | 
						|
    return quantum_api(request).show_port_attachment(network_id, port_id)
 | 
						|
 | 
						|
 | 
						|
def get_vif_ids(request):
 | 
						|
    vifs = []
 | 
						|
    attached_vifs = []
 | 
						|
    # Get a list of all networks
 | 
						|
    networks_list = quantum_api(request).list_networks()
 | 
						|
    for network in networks_list['networks']:
 | 
						|
        ports = quantum_api(request).list_ports(network['id'])
 | 
						|
        # Get port attachments
 | 
						|
        for port in ports['ports']:
 | 
						|
            port_attachment = quantum_api(request).show_port_attachment(
 | 
						|
                                                    network['id'],
 | 
						|
                                                    port['id'])
 | 
						|
            if port_attachment['attachment']:
 | 
						|
                attached_vifs.append(
 | 
						|
                    port_attachment['attachment']['id'].encode('ascii'))
 | 
						|
    # Get all instances
 | 
						|
    instances = server_list(request)
 | 
						|
    # Get virtual interface ids by instance
 | 
						|
    for instance in instances:
 | 
						|
        id = instance.id
 | 
						|
        instance_vifs = extras_api(request).virtual_interfaces.list(id)
 | 
						|
        for vif in instance_vifs:
 | 
						|
            # Check if this VIF is already connected to any port
 | 
						|
            if str(vif.id) in attached_vifs:
 | 
						|
                vifs.append({
 | 
						|
                    'id': vif.id,
 | 
						|
                    'instance': instance.id,
 | 
						|
                    'instance_name': instance.name,
 | 
						|
                    'available': False
 | 
						|
                })
 | 
						|
            else:
 | 
						|
                vifs.append({
 | 
						|
                    'id': vif.id,
 | 
						|
                    'instance': instance.id,
 | 
						|
                    'instance_name': instance.name,
 | 
						|
                    'available': True
 | 
						|
                })
 | 
						|
    return vifs
 | 
						|
 | 
						|
 | 
						|
class GlobalSummary(object):
 | 
						|
    node_resources = ['vcpus', 'disk_size', 'ram_size']
 | 
						|
    unit_mem_size = {'disk_size': ['GiB', 'TiB'], 'ram_size': ['MiB', 'GiB']}
 | 
						|
    node_resource_info = ['', 'active_', 'avail_']
 | 
						|
 | 
						|
    def __init__(self, request):
 | 
						|
        self.summary = {}
 | 
						|
        for rsrc in GlobalSummary.node_resources:
 | 
						|
            for info in GlobalSummary.node_resource_info:
 | 
						|
                self.summary['total_' + info + rsrc] = 0
 | 
						|
        self.request = request
 | 
						|
        self.service_list = []
 | 
						|
        self.usage_list = []
 | 
						|
 | 
						|
    def service(self):
 | 
						|
        try:
 | 
						|
            self.service_list = service_list(self.request)
 | 
						|
        except api_exceptions.ApiException, e:
 | 
						|
            self.service_list = []
 | 
						|
            LOG.exception('ApiException fetching service list in instance usage')
 | 
						|
            messages.error(self.request,
 | 
						|
                           'Unable to get service info: %s' % e.message)
 | 
						|
            return
 | 
						|
 | 
						|
        for service in self.service_list:
 | 
						|
            if service.type == 'nova-compute':
 | 
						|
                self.summary['total_vcpus'] += min(service.stats['max_vcpus'],
 | 
						|
                        service.stats.get('vcpus', 0))
 | 
						|
                self.summary['total_disk_size'] += min(
 | 
						|
                        service.stats['max_gigabytes'],
 | 
						|
                        service.stats.get('local_gb', 0))
 | 
						|
                self.summary['total_ram_size'] += min(
 | 
						|
                        service.stats['max_ram'],
 | 
						|
                        service.stats['memory_mb']) if 'max_ram' \
 | 
						|
                                in service.stats \
 | 
						|
                                else service.stats.get('memory_mb', 0)
 | 
						|
 | 
						|
    def usage(self, datetime_start, datetime_end):
 | 
						|
        try:
 | 
						|
            self.usage_list = usage_list(self.request, datetime_start,
 | 
						|
                    datetime_end)
 | 
						|
        except api_exceptions.ApiException, e:
 | 
						|
            self.usage_list = []
 | 
						|
            LOG.exception('ApiException fetching usage list in instance usage'
 | 
						|
                      ' on date range "%s to %s"' % (datetime_start,
 | 
						|
                                                     datetime_end))
 | 
						|
            messages.error(self.request,
 | 
						|
                    'Unable to get usage info: %s' % e.message)
 | 
						|
            return
 | 
						|
 | 
						|
        for usage in self.usage_list:
 | 
						|
            # FIXME: api needs a simpler dict interface (with iteration)
 | 
						|
            # - anthony
 | 
						|
            # NOTE(mgius): Changed this on the api end.  Not too much
 | 
						|
            # neater, but at least its not going into private member
 | 
						|
            # data of an external class anymore
 | 
						|
            # usage = usage._info
 | 
						|
            for k in usage._attrs:
 | 
						|
                v = usage.__getattr__(k)
 | 
						|
                if type(v) in [float, int]:
 | 
						|
                    if not k in self.summary:
 | 
						|
                        self.summary[k] = 0
 | 
						|
                    self.summary[k] += v
 | 
						|
 | 
						|
    def human_readable(self, rsrc):
 | 
						|
        if self.summary['total_' + rsrc] > 1023:
 | 
						|
            self.summary['unit_' + rsrc] = GlobalSummary.unit_mem_size[rsrc][1]
 | 
						|
            mult = 1024.0
 | 
						|
        else:
 | 
						|
            self.summary['unit_' + rsrc] = GlobalSummary.unit_mem_size[rsrc][0]
 | 
						|
            mult = 1.0
 | 
						|
 | 
						|
        for kind in GlobalSummary.node_resource_info:
 | 
						|
            self.summary['total_' + kind + rsrc + '_hr'] = \
 | 
						|
                    self.summary['total_' + kind + rsrc] / mult
 | 
						|
 | 
						|
    def avail(self):
 | 
						|
        for rsrc in GlobalSummary.node_resources:
 | 
						|
            self.summary['total_avail_' + rsrc] = \
 | 
						|
                    self.summary['total_' + rsrc] - \
 | 
						|
                    self.summary['total_active_' + rsrc]
 |