Files
occi-os/api/compute/compute_resource.py
Thijs Metsch fd310fc56b fixes #14
2012-07-02 10:32:28 +02:00

336 lines
12 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (c) 2012, Intel Performance Learning Solutions Ltd.
#
# 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.
"""
The compute resource backend for OpenStack.
"""
#pylint: disable=W0232,R0201
import logging
import uuid
from api.extensions import openstack, occi_future
from api.compute import templates
from nova_glue import net
from nova_glue import vm
from nova_glue import vol
from occi import core_model
from occi.backend import KindBackend, ActionBackend
from occi.extensions import infrastructure
LOG = logging.getLogger('api.compute.compute_resource')
class ComputeBackend(KindBackend, ActionBackend):
"""
The compute backend.
"""
def create(self, entity, extras):
"""
Create a VM.
"""
LOG.debug('Creating an Virtual machine')
# ignore some attributes - done via templating
if 'occi.compute.cores' in entity.attributes or \
'occi.compute.speed' in entity.attributes or \
'occi.compute.memory' in entity.attributes or \
'occi.compute.architecture' in entity.attributes:
raise AttributeError('There are unsupported attributes in the '
'request.')
# create the VM
context = extras['nova_ctx']
instance = vm.create_vm(entity, context)
uid = instance['uuid']
# deal with some networking stuff
net_info = net.get_adapter_info(uid, context)
attach_to_default_network(net_info, entity, extras)
# add consoles and storage links
set_console_info(entity, uid, extras)
# set some attributes
entity.attributes['occi.core.id'] = instance['uuid']
entity.attributes['occi.compute.hostname'] = instance['hostname']
entity.attributes['occi.compute.architecture'] = \
vol.get_image_architecture(uid, extras['nova_ctx'])
entity.attributes['occi.compute.cores'] = str(instance['vcpus'])
entity.attributes['occi.compute.speed'] = str(0.0) # N/A in instance
value = str(float(instance['memory_mb']) / 1024)
entity.attributes['occi.compute.memory'] = value
entity.attributes['occi.compute.state'] = 'inactive'
# set valid actions
entity.actions = [infrastructure.STOP,
infrastructure.SUSPEND,
infrastructure.RESTART,
openstack.OS_REVERT_RESIZE,
openstack.OS_CONFIRM_RESIZE,
openstack.OS_CREATE_IMAGE]
def retrieve(self, entity, extras):
"""
Retrieve a VM.
"""
uid = entity.attributes['occi.core.id']
context = extras['nova_ctx']
instance = vm.get_vm(uid, context)
LOG.debug('Retrieving an Virtual machine: ', uid)
# set state and applicable actions!
state, actions = vm.get_occi_state(uid, context)
entity.attributes['occi.compute.state'] = state
entity.actions = actions
# set up to date attributes
entity.attributes['occi.compute.cores'] = str(instance['vcpus'])
value = str(float(instance['memory_mb']) / 1024)
entity.attributes['occi.compute.memory'] = value
#Now we have the instance state, get its updated network info
net_info = net.get_adapter_info(uid, context)
attach_to_default_network(net_info, entity, extras)
# add consoles and storage links
set_console_info(entity, uid, extras)
def update(self, old, new, extras):
"""
Update an VM.
"""
context = extras['nova_ctx']
uid = old.attributes['occi.core.id']
LOG.debug('Updating an Virtual machine: ', uid)
# update title, summary etc.
if len(new.attributes['occi.core.title']) > 0:
old.attributes['occi.core.title'] = \
new.attributes['occi.core.title']
if len(new.attributes['occi.core.summary']) > 0:
old.attributes['occi.core.summary'] = \
new.attributes['occi.core.summary']
# for now we will only handle one mixin change per request
mixin = new.mixins[0]
if isinstance(mixin, templates.ResourceTemplate):
flavor_name = mixin.term
vm.resize_vm(uid, flavor_name, context)
old.attributes['occi.compute.state'] = 'inactive'
# now update the mixin info
# TODO(tmetsch): remove old mixin!!!
old.mixins.append(mixin)
elif isinstance(mixin, templates.OsTemplate):
image_href = mixin.os_id
vm.rebuild_vm(uid, image_href, context)
old.attributes['occi.compute.state'] = 'inactive'
#now update the mixin info
# TODO(tmetsch): remove old mixin!!!
old.mixins.append(mixin)
else:
msg = ('Unrecognized mixin. %s') % str(mixin)
LOG.error(msg)
raise AttributeError(msg)
def replace(self, old, new, extras):
"""
XXX:not doing anything - full updates are hard :-)
"""
pass
def delete(self, entity, extras):
"""
Remove a VM.
"""
msg = ('Removing representation of virtual machine with id: %s') %\
entity.identifier
LOG.info(msg)
context = extras['nova_ctx']
uid = entity.attributes['occi.core.id']
vm.delete_vm(uid, context)
def action(self, entity, action, attributes, extras):
"""
Perform an action.
"""
# As there is no callback mechanism to update the state
# of computes known by occi, a call to get the latest representation
# must be made.
context = extras['nova_ctx']
uid = entity.attributes['occi.core.id']
# set state and applicable actions - so even if the user hasn't done
# a GET het can still the most applicable action now...
state, actions = vm.get_occi_state(uid, context)
entity.attributes['occi.compute.state'] = state
entity.actions = actions
if action not in entity.actions:
raise AttributeError("This action is currently not applicable.")
elif action == infrastructure.START:
state = entity.attributes['occi.compute.state']
vm.start_vm(uid, state, context)
entity.attributes['occi.compute.state'] = 'active'
entity.actions = [infrastructure.STOP,
infrastructure.SUSPEND,
infrastructure.RESTART,
openstack.OS_REVERT_RESIZE,
openstack.OS_CONFIRM_RESIZE,
openstack.OS_CREATE_IMAGE]
elif action == infrastructure.STOP:
vm.stop_vm(uid, context)
entity.attributes['occi.compute.state'] = 'inactive'
entity.actions = [infrastructure.START]
elif action == infrastructure.RESTART:
if not 'method' in attributes:
raise AttributeError('Please provide a method!')
method = attributes['method']
vm.restart_vm(uid, method, context)
entity.attributes['occi.compute.state'] = 'inactive'
entity.actions = []
elif action == infrastructure.SUSPEND:
vm.suspend_vm(uid, context)
entity.attributes['occi.compute.state'] = 'suspended'
entity.actions = [infrastructure.START]
# SOME HELPER FUNCTIONS
def attach_to_default_network(vm_net_info, entity, extras):
"""
Associates a network adapter with the relevant network resource.
"""
# check that existing network does not exist
if len(entity.links) > 0:
for link in entity.links:
if link.kind.term == infrastructure.NETWORKINTERFACE.term and \
link.kind.scheme == infrastructure.NETWORK.scheme:
msg = 'A link to the network already exists. Will update ' \
'the links attributes.'
LOG.debug(msg)
link.attributes['occi.networkinterface.interface'] = \
vm_net_info['vm_iface']
link.attributes['occi.networkinterface.address'] = \
vm_net_info['address']
link.attributes['occi.networkinterface.gateway'] = \
vm_net_info['gateway']
link.attributes['occi.networkinterface.mac'] = \
vm_net_info['mac']
return
# If the network association does not exist...
# Get a handle to the default network
registry = extras['registry']
default_network = registry.get_resource('/network/DEFAULT_NETWORK', None)
source = entity
target = default_network
# Create the link to the default network
identifier = str(uuid.uuid4())
link = core_model.Link(identifier, infrastructure.NETWORKINTERFACE,
[infrastructure.IPNETWORKINTERFACE], source, target)
link.attributes['occi.core.id'] = identifier
link.attributes['occi.networkinterface.interface'] = \
vm_net_info['vm_iface']
link.attributes['occi.networkinterface.mac'] = vm_net_info['mac']
link.attributes['occi.networkinterface.state'] = 'active'
link.attributes['occi.networkinterface.address'] = \
vm_net_info['address']
link.attributes['occi.networkinterface.gateway'] = \
vm_net_info['gateway']
link.attributes['occi.networkinterface.allocation'] = \
vm_net_info['allocation']
entity.links.append(link)
registry.add_resource(identifier, link, extras)
def set_console_info(entity, uid, extras):
"""
Adds console access information to the resource.
"""
address = entity.links[0].attributes['occi.networkinterface.address']
ssh_console_present = False
vnc_console_present = False
comp_sch = 'http://schemas.openstack.org/occi/infrastructure/compute#'
for link in entity.links:
if link.target.kind.term == "ssh_console" and link.target.kind\
.scheme == comp_sch:
ssh_console_present = True
elif link.target.kind.term == "vnc_console" and link.target.kind\
.scheme == comp_sch:
vnc_console_present = True
registry = extras['registry']
if not ssh_console_present:
identifier = str(uuid.uuid4())
ssh_console = core_model.Resource(
identifier, occi_future.SSH_CONSOLE, [],
links=None, summary='',
title='')
ssh_console.attributes['occi.core.id'] = identifier
ssh_console.attributes['org.openstack.compute.console.ssh'] = \
'ssh://' + address + ':22'
registry.add_resource(identifier, ssh_console, extras)
identifier = str(uuid.uuid4())
ssh_console_link = core_model.Link(
identifier,
occi_future.CONSOLE_LINK,
[], entity, ssh_console)
ssh_console_link.attributes['occi.core.id'] = identifier
registry.add_resource(identifier, ssh_console_link, extras)
entity.links.append(ssh_console_link)
if not vnc_console_present:
console = vm.get_vnc(uid, extras['nova_ctx'])
if console is None:
return
identifier = str(uuid.uuid4())
vnc_console = core_model.Resource(
identifier, occi_future.VNC_CONSOLE, [],
links=None, summary='',
title='')
vnc_console.attributes['occi.core.id'] = identifier
vnc_console.attributes['org.openstack.compute.console.vnc'] = \
console['url']
registry.add_resource(identifier, vnc_console, extras)
identifier = str(uuid.uuid4())
vnc_console_link = core_model.Link(
identifier,
occi_future.CONSOLE_LINK,
[], entity, vnc_console)
vnc_console_link.attributes['occi.core.id'] = identifier
registry.add_resource(identifier, vnc_console_link, extras)
entity.links.append(vnc_console_link)