Files
occi-os/api/compute/computeresource.py
2012-06-26 20:35:55 +02:00

816 lines
34 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.
import uuid
from occi import backend
from occi import core_model
from occi.extensions import infrastructure
from webob import exc
from api.compute import templates
from api.extensions import occi_future
from api.extensions import openstack as os_extns
from nova import compute
from nova.compute import instance_types
from nova.compute import task_states
from nova.compute import vm_states
from nova import exception
from nova import flags
from nova import image
from nova import utils
from nova import log as logging
from nova.network import api as net_api
FLAGS = flags.FLAGS
# Hi I'm a logger, use me! :-)
LOG = logging.getLogger('nova.api.occi.backends.compute')
class ComputeBackend(backend.KindBackend, backend.ActionBackend):
"""
A Backend for compute instances.
"""
def __init__(self):
super(ComputeBackend, self).__init__()
self.compute_api = compute.API()
self.network_api = net_api.API()
def _check_invalid_attrs(self, resource):
"""
If certain OCCI attributes are received then
an attribute exception is raised.
"""
if (('occi.compute.cores' in resource.attributes) or
('occi.compute.speed' in resource.attributes) or
('occi.compute.memory' in resource.attributes) or
('occi.compute.architecture' in resource.attributes)):
msg = _('There are unsupported attributes in the request.')
LOG.error(msg)
raise AttributeError(msg)
def create(self, resource, extras):
"""
creates the VM!
If a request arrives with explicit values for certain attrs
like occi.compute.cores then a bad request must be issued as
OpenStack does not support this.
"""
resource.links = []
msg = _('Creating the virtual machine.')
LOG.info(msg)
self._check_invalid_attrs(resource)
name = (resource.attributes['occi.compute.hostname']
if 'occi.compute.hostname' in resource.attributes else None)
key_name = key_data = None
#Auto-gen'ed 1st. If OCCI extension supplied this will overwrite this
password = utils.generate_password(FLAGS.password_length)
access_ip_v4 = None
access_ip_v6 = None
# Would be good to specify user_data via OCCI. Look to use
# CompatibleOne extensions spec.
user_data = None
metadata = {}
injected_files = []
min_count = max_count = 1
requested_networks = None
sg_names = []
availability_zone = None
config_drive = None
block_device_mapping = None
# these can be specified through OS Templates
kernel_id = ramdisk_id = None
auto_disk_config = None
scheduler_hints = None
# extract mixin information
rc = oc = 0
for mixin in resource.mixins:
if isinstance(mixin, templates.ResourceTemplate):
r = mixin
rc += 1
elif isinstance(mixin, templates.OsTemplate):
os_tpl = mixin
oc += 1
elif mixin == os_extns.OS_KEY_PAIR_EXT:
attr = 'org.openstack.credentials.publickey.name'
key_name = \
resource.attributes[attr]
attr = 'org.openstack.credentials.publickey.data'
key_data = \
resource.attributes[attr]
elif mixin == os_extns.OS_ADMIN_PWD_EXT:
password = \
resource.attributes['org.openstack.credentials.admin_pwd']
elif mixin == os_extns.OS_ACCESS_IP_EXT:
attr = 'org.openstack.network.access.version'
if resource.attributes[attr] == 'ipv4':
access_ip_v4 = \
resource.attributes['org.openstack.network.access.ip']
elif resource.attributes[attr] == 'ipv6':
access_ip_v6 = \
resource.attributes['org.openstack.network.access.ip']
else:
raise exc.HTTPBadRequest()
#Look for security group. If the group is non-existant, the
#call to create will fail.
if occi_future.SEC_GROUP in mixin.related:
sg_names.append(mixin.term)
if rc < 1 and oc < 1:
msg = _('No resource or OS template in the request.')
LOG.error(msg)
exc.HTTPBadRequest()
if rc > 1 or oc > 1:
msg = _('More than one resource/OS template in the request.')
LOG.error(msg)
raise AttributeError(msg=unicode(msg))
#If no security group, ensure the default is applied
if len(sg_names) == 0:
sg_names.append('default')
flavor_name = r.term
os_tpl_url = os_tpl.os_id
sg_names = list(set(sg_names))
try:
if flavor_name:
inst_type = \
instance_types.get_instance_type_by_name(flavor_name)
else:
inst_type = instance_types.get_default_instance_type()
msg = _('No resource template was found in the request. '
'Using the default: %s') % inst_type['name']
LOG.warn(msg)
(instances, _reservation_id) = self.compute_api.create(
context=extras['nova_ctx'],
instance_type=inst_type,
image_href=os_tpl_url,
kernel_id=kernel_id,
ramdisk_id=ramdisk_id,
min_count=min_count,
max_count=max_count,
display_name=name,
display_description=name,
key_name=key_name,
key_data=key_data,
security_group=sg_names,
availability_zone=availability_zone,
user_data=user_data,
metadata=metadata,
injected_files=injected_files,
admin_password=password,
block_device_mapping=block_device_mapping,
access_ip_v4=access_ip_v4,
access_ip_v6=access_ip_v6,
requested_networks=requested_networks,
config_drive=config_drive,
auto_disk_config=auto_disk_config,
scheduler_hints=scheduler_hints)
except exception.QuotaError as error:
self._handle_quota_error(error)
except exception.InstanceTypeMemoryTooSmall as error:
raise exc.HTTPBadRequest(explanation=unicode(error))
except exception.InstanceTypeDiskTooSmall as error:
raise exc.HTTPBadRequest(explanation=unicode(error))
except exception.ImageNotFound as error:
msg = _("Can not find requested image")
raise exc.HTTPBadRequest(explanation=msg)
except exception.FlavorNotFound as error:
msg = _("Invalid flavor provided.")
raise exc.HTTPBadRequest(explanation=msg)
except exception.KeypairNotFound as error:
msg = _("Invalid key_name provided.")
raise exc.HTTPBadRequest(explanation=msg)
except exception.SecurityGroupNotFound as error:
raise exc.HTTPBadRequest(explanation=unicode(error))
except rpc_common.RemoteError as err:
msg = "%(err_type)s: %(err_msg)s" % \
{'err_type': err.exc_type, 'err_msg': err.value}
raise exc.HTTPBadRequest(explanation=msg)
#add resource attribute values
resource.attributes['occi.core.id'] = instances[0]['uuid']
resource.attributes['occi.compute.hostname'] = instances[0]['hostname']
resource.attributes['occi.compute.architecture'] = \
self._get_vm_arch(extras['nova_ctx'],
os_tpl)
resource.attributes['occi.compute.cores'] = str(instances[0]['vcpus'])
# TODO(dizz): possible blueprint?
# occi.compute.speed is not available in instances by
# default.CPU speed is not available but could be made available
# through: db::nova::compute_nodes::cpu_info
# Additional code is required in:
# nova/nova/virt/libvirt/connection.py::get_cpu_info()
msg = _('Cannot tell what the CPU speed is.')
LOG.info(msg)
resource.attributes['occi.compute.speed'] = str(0.0)
resource.attributes['occi.compute.memory'] = \
str(float(instances[0]['memory_mb']) / 1024)
# the resource is not necessarily active at this stage. review.
resource.attributes['occi.compute.state'] = 'active'
# Once created, the VM is attached to a public network with an
# addresses allocated by DHCP
# A link is created to this network (IP) and set the ip to that of the
# allocated ip
vm_net_info = self._get_adapter_info(instances[0], extras)
self._attach_to_default_network(vm_net_info, resource, extras)
self._get_console_info(resource, instances[0], extras)
self._attach_to_local_storage(inst_type)
#set valid actions
resource.actions = [infrastructure.STOP,
infrastructure.SUSPEND,
infrastructure.RESTART,
os_extns.OS_REVERT_RESIZE,
os_extns.OS_CONFIRM_RESIZE,
os_extns.OS_CREATE_IMAGE]
def _attach_to_local_storage(self, inst_type):
"""
Associate ephemeral or root storage with compute instance
"""
# TODO(dizz): if there is ephemeral or root storage, assciate it!
pass
def _get_vm_arch(self, context, os_template_mixin):
"""
Extract architecture from either:
- image name, title or metadata. The architecture is sometimes
encoded in the image's name
- db::glance::image_properties could be used reliably so long as the
information is supplied when registering an image with glance.
Heuristic:
- if term, title or description has x86_32 or x86_x64 then the arch
is x86 or x64 respectively.
- if associated OS image has properties arch or architecture that
equal x86 or x64.
- else return a default of x86
"""
arch = ''
if ((os_template_mixin.term.find('x86_64')
or os_template_mixin.title.find('x86_64')) >= 0):
arch = 'x64'
elif ((os_template_mixin.term.find('x86_32')
or os_template_mixin.title.find('x86_32')) >= 0):
arch = 'x86'
else:
image_service = image.get_default_image_service()
img = image_service.show(context, os_template_mixin.os_id)
img_props = img['properties']
if ('arch' in img_props):
arch = img['properties']['arch']
elif ('architecture' in img_props):
arch = img['properties']['architecture']
# if all attempts fail set it to a default value
if arch == '':
arch = 'x86'
return arch
def _get_adapter_info(self, instance, extras):
"""
Extracts the VMs network adapter information: interface name,
IP address, gateway and mac address.
"""
# TODO(dizz): currently this assumes one adapter on the VM.
# It's likely that this will not be the case when using Quantum
vm_net_info = {'vm_iface': '', 'address': '', 'gateway': '', 'mac': '',
'allocation': ''}
sj = self.network_api.get_instance_nw_info(extras['nova_ctx'],
instance)
#catches an odd error whereby no network info is returned back
if len(sj) <= 0:
msg = _('No network info was returned either live or cached.')
LOG.warn(msg)
return vm_net_info
vm_net_info['vm_iface'] = sj[0]['network']['meta']['bridge_interface']
#OS-specific if a VM is stopped it has no IP address
if len(sj[0]['network']['subnets'][0]['ips']) > 0:
vm_net_info['address'] = \
sj[0]['network']['subnets'][0]['ips'][0]['address']
else:
vm_net_info['address'] = ''
vm_net_info['gateway'] = \
sj[0]['network']['subnets'][0]['gateway']['address']
vm_net_info['mac'] = sj[0]['address']
if sj[0]['network']['subnets'][0]['ips'][0]['type'] == 'fixed':
vm_net_info['allocation'] = 'static'
else:
vm_net_info['allocation'] = 'dynamic'
return vm_net_info
def _attach_to_default_network(self, vm_net_info, resource, extras):
"""
Associates a network adapter with the relevant network resource.
"""
# check that existing network does not exist
scheme = "http://schemas.ogf.org/occi/infrastructure#"
if len(resource.links) > 0:
for link in resource.links:
if (link.kind.term == "networkinterface"
and link.kind.scheme == 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 = resource
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']
resource.links.append(link)
registry.add_resource(identifier, link, extras)
def _get_console_info(self, resource, instance, extras):
"""
Adds console access information to the resource.
"""
address = resource.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 resource.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
if not ssh_console_present:
registry = extras['registry']
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,
[], resource, ssh_console)
ssh_console_link.attributes['occi.core.id'] = identifier
registry.add_resource(identifier, ssh_console_link, extras)
resource.links.append(ssh_console_link)
if not vnc_console_present:
try:
console = self.compute_api.get_vnc_console(extras['nova_ctx'],
instance, 'novnc')
except Exception:
msg = _('Console info is not available yet.')
LOG.debug(msg)
return
registry = extras['registry']
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,
[], resource, vnc_console)
vnc_console_link.attributes['occi.core.id'] = identifier
registry.add_resource(identifier, vnc_console_link, extras)
resource.links.append(vnc_console_link)
def _handle_quota_error(self, error):
"""
Reraise quota errors as api-specific http exceptions
"""
# Note this is a direct lift from nova/api/openstack/compute/servers.py
# however as it is protected we cannot import it :-(
code_mappings = {
"OnsetFileLimitExceeded":
_("Personality file limit exceeded"),
"OnsetFilePathLimitExceeded":
_("Personality file path too long"),
"OnsetFileContentLimitExceeded":
_("Personality file content too long"),
# NOTE(bcwaldon): expose the message generated below in order
# to better explain how the quota was exceeded
"InstanceLimitExceeded": error.message,
}
expl = code_mappings.get(error.kwargs['code'], error.message)
raise exc.HTTPRequestEntityTooLarge(explanation=expl,
headers={'Retry-After': 0})
def retrieve(self, entity, extras):
"""
Prepares the resource representation ready for pyssf rendering
"""
context = extras['nova_ctx']
uid = entity.attributes['occi.core.id']
try:
instance = self.compute_api.get(context, uid)
except exception.NotFound:
raise exc.HTTPNotFound()
# See nova/compute/vm_states.py nova/compute/task_states.py
#
# Mapping assumptions:
# - active == VM can service requests from network. These requests
# can be from users or VMs
# - inactive == the oppose! :-)
# - suspended == machine in a frozen state e.g. via suspend or pause
# change password - OS
# confirm resized server
if instance['vm_state'] in (vm_states.ACTIVE,
task_states.UPDATING_PASSWORD,
task_states.RESIZE_VERIFY):
entity.attributes['occi.compute.state'] = 'active'
entity.actions = [infrastructure.STOP,
infrastructure.SUSPEND,
infrastructure.RESTART,
os_extns.OS_CONFIRM_RESIZE,
os_extns.OS_REVERT_RESIZE,
os_extns.OS_CHG_PWD,
os_extns.OS_CREATE_IMAGE]
# reboot server - OS, OCCI
# start server - OCCI
elif instance['vm_state'] in (task_states.STARTING,
task_states.POWERING_ON,
task_states.REBOOTING,
task_states.REBOOTING_HARD):
entity.attributes['occi.compute.state'] = 'inactive'
entity.actions = []
# pause server - OCCI, suspend server - OCCI, stop server - OCCI
elif instance['vm_state'] in (task_states.STOPPING,
task_states.POWERING_OFF):
entity.attributes['occi.compute.state'] = 'inactive'
entity.actions = [infrastructure.START]
# resume server - OCCI
elif instance['vm_state'] in (task_states.RESUMING,
task_states.PAUSING,
task_states.SUSPENDING):
entity.attributes['occi.compute.state'] = 'suspended'
if instance['vm_state'] in (vm_states.PAUSED,
vm_states.SUSPENDED):
entity.actions = [infrastructure.START]
else:
entity.actions = []
# rebuild server - OS
# resize server confirm rebuild
# revert resized server - OS (indirectly OCCI)
elif instance['vm_state'] in (
vm_states.RESIZING,
vm_states.REBUILDING,
task_states.RESIZE_CONFIRMING,
task_states.RESIZE_FINISH,
task_states.RESIZE_MIGRATED,
task_states.RESIZE_MIGRATING,
task_states.RESIZE_PREP,
task_states.RESIZE_REVERTING):
entity.attributes['occi.compute.state'] = 'inactive'
entity.actions = []
#Now we have the instance state, get its updated network info
vm_net_info = self._get_adapter_info(instance, extras)
self._attach_to_default_network(vm_net_info, entity, extras)
self._get_console_info(entity, instance, extras)
return instance
def delete(self, entity, extras):
"""
Deletes the referenced 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']
try:
instance = self.compute_api.get(context, uid)
except exception.NotFound:
raise exc.HTTPNotFound()
if FLAGS.reclaim_instance_interval:
self.compute_api.soft_delete(context, instance)
else:
self.compute_api.delete(context, instance)
def update(self, old, new, extras):
"""
Updates basic attribute information, resizes the VM and rebuilds the
VM. Only one mixin update per execution.
"""
msg = _('Partial update requested for instance: %s') % \
old.attributes['occi.core.id']
LOG.info(msg)
instance = self.retrieve(old, extras)
if len(new.attributes) > 0:
self._update_attrs(old, new)
# for now we will only handle one mixin change per request
mixin = new.mixins[0]
if isinstance(mixin, templates.ResourceTemplate):
self._update_resize_vm(old, extras, instance, mixin)
elif isinstance(mixin, templates.OsTemplate):
# do we need to check for new os rebuild in new?
self._update_rebuild_vm(old, extras, instance, mixin)
elif isinstance(mixin, occi_future.UserSecurityGroupMixin):
#TODO(dizz): should we implement this here?
msg = _('Updating security rule group')
LOG.info(msg)
raise exc.HTTPBadRequest()
else:
tmpl = '%s%s' % (mixin.scheme, mixin.term)
msg = _('Unrecognised mixin. %s') % tmpl
LOG.error()
raise exc.HTTPBadRequest()
def _update_attrs(self, old, new):
"""
Updates basic attributes. Supports only title and summary changes.
"""
msg = _('Updating mutable attributes of instance')
LOG.info(msg)
if (('occi.core.title' in new.attributes)
or ('occi.core.title' in new.attributes)):
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']
else:
msg = _('Cannot update the supplied attributes.')
LOG.error(msg)
raise exc.HTTPBadRequest
def _update_resize_vm(self, old, extras, instance, mixin):
"""
Resizes up or down a VM
Update: libvirt now supports resize see:
http://wiki.openstack.org/HypervisorSupportMatrix
"""
msg = _('Resize requested')
LOG.info(msg)
flavor = instance_types.get_instance_type_by_name(mixin.term)
kwargs = {}
try:
self.compute_api.resize(extras['nova_ctx'], instance,
flavor_id=flavor['flavorid'], **kwargs)
except exception.FlavorNotFound:
msg = _("Unable to locate requested flavor.")
raise exc.HTTPBadRequest(explanation=msg)
except exception.CannotResizeToSameSize:
msg = _("Resize requires a change in size.")
raise exc.HTTPBadRequest(explanation=msg)
except exception.InstanceInvalidState:
exc.HTTPConflict()
old.attributes['occi.compute.state'] = 'inactive'
#now update the mixin info
for m in old.mixins:
if m.term == mixin.term and m.scheme == mixin.scheme:
m = mixin
tmpl = '%s%s' % (m.scheme, m.term)
msg = _('Resource template is changed: %s') % tmpl
LOG.debug(msg)
def _update_rebuild_vm(self, old, extras, instance, mixin):
"""
Rebuilds the specified VM with the supplied OsTemplate mixin.
"""
# TODO(dizz): Use the admin_password extension?
msg = _('Rebuild requested')
LOG.info(msg)
image_href = mixin.os_id
admin_password = utils.generate_password(FLAGS.password_length)
kwargs = {}
try:
self.compute_api.rebuild(extras['nova_ctx'], instance,
image_href, admin_password, **kwargs)
except exception.InstanceInvalidState:
exc.HTTPConflict()
except exception.InstanceNotFound:
msg = _("Instance could not be found")
raise exc.HTTPNotFound(explanation=msg)
except exception.ImageNotFound:
msg = _("Cannot find image for rebuild")
raise exc.HTTPBadRequest(explanation=msg)
old.attributes['occi.compute.state'] = 'inactive'
#now update the mixin info
for m in old.mixins:
if m.term == mixin.term and m.scheme == mixin.scheme:
m = mixin
tmpl = '%s%s' % (m.scheme, m.term)
msg = _('OS template is changed: %s') % tmpl
LOG.debug(msg)
def action(self, entity, action, attributes, extras):
"""
Executed when a request for an action against a resource is received.
"""
# 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.
instance = self.retrieve(entity, extras)
context = extras['nova_ctx']
if action not in entity.actions:
raise AttributeError("This action is not currently applicable.")
elif action == infrastructure.START:
self._start_vm(entity, instance, context)
elif action == infrastructure.STOP:
self._stop_vm(entity, attributes, instance, context)
elif action == infrastructure.RESTART:
self._restart_vm(entity, attributes, instance, context)
elif action == infrastructure.SUSPEND:
self._suspend_vm(entity, attributes, instance, context)
else:
raise exc.HTTPBadRequest()
def _start_vm(self, entity, instance, context):
"""
Starts a vm that is in the stopped state. Note, currently we do not
use the nova start and stop, rather the resume/suspend methods. The
start action also unpauses a paused VM.
"""
msg = _('Starting virtual machine with id %s') % entity.identifier
LOG.info(msg)
try:
if entity.attributes['occi.compute.state'] == 'suspended':
self.compute_api.unpause(context, instance)
else:
self.compute_api.resume(context, instance)
except Exception:
msg = _('Error in starting VM')
LOG.error(msg)
raise exc.HTTPServerError()
entity.attributes['occi.compute.state'] = 'active'
entity.actions = [infrastructure.STOP,
infrastructure.SUSPEND,
infrastructure.RESTART,
os_extns.OS_REVERT_RESIZE,
os_extns.OS_CONFIRM_RESIZE,
os_extns.OS_CREATE_IMAGE]
def _stop_vm(self, entity, attributes, instance, context):
"""
Stops a VM. Rather than use stop, suspend is used.
OCCI -> graceful, acpioff, poweroff
OS -> unclear
"""
msg = _('Stopping virtual machine with id %s') % entity.identifier
LOG.info(msg)
if 'method' in attributes:
msg = _('OS only allows one type of stop. '
'What is specified in the request will be ignored.')
LOG.info(msg)
try:
# TODO(dizz): There are issues with the stop and start methods of
# OS. For now we'll use suspend.
# self.compute_api.stop(context, instance)
self.compute_api.suspend(context, instance)
except Exception:
msg = _('Error in stopping VM')
LOG.error(msg)
raise exc.HTTPServerError()
entity.attributes['occi.compute.state'] = 'inactive'
entity.actions = [infrastructure.START]
def _restart_vm(self, entity, attributes, instance, context):
"""
Restarts a VM.
OS types == SOFT, HARD
OCCI -> graceful, warm and cold
mapping:
- SOFT -> graceful, warm
- HARD -> cold
"""
msg = _('Restarting virtual machine with id %s') % entity.identifier
LOG.info(msg)
if not 'method' in attributes:
raise exc.HTTPBadRequest()
if attributes['method'] in ('graceful', 'warm'):
reboot_type = 'SOFT'
elif attributes['method'] is 'cold':
reboot_type = 'HARD'
else:
raise exc.HTTPBadRequest()
try:
self.compute_api.reboot(context, instance, reboot_type)
except exception.InstanceInvalidState:
exc.HTTPConflict()
except Exception as e:
msg = _("Error in reboot %s") % e
LOG.exception(msg)
raise exc.HTTPUnprocessableEntity()
entity.attributes['occi.compute.state'] = 'inactive'
entity.actions = []
def _suspend_vm(self, entity, attributes, instance, context):
"""
Suspends a VM. Use the start action to unsuspend a VM.
"""
msg = _('Stopping (suspending) virtual machine with id %s') % \
entity.identifier
LOG.info(msg)
if 'method' in attributes:
msg = _('OS only allows one type of suspend. '
'What is specified in the request will be ignored.')
LOG.info(msg)
try:
self.compute_api.pause(context, instance)
except Exception:
msg = _('Error in stopping VM.')
LOG.error(msg)
raise exc.HTTPServerError()
entity.attributes['occi.compute.state'] = 'suspended'
entity.actions = [infrastructure.START]