openstack框架可以很容易地扩展api,可以自定义一些api。本文以nova-api为例,进行api扩展。
nova的扩展都放在nova/api/openstack/compute/contrib/ 目录下,比如我们扩展一个注入文件的api。
nova/api/openstack/compute/contrib/inject_file.py
import webob
from webob import exc
from nova import db
from nova import exception
from nova import compute
from nova.api.openstack import extensions
from nova.api.openstack import common
from nova.api.openstack import wsgi
from nova.compute import power_state
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
# 这个操作需要认证
authorize = extensions.extension_authorizer('compute', 'inject_file')
class InjectFileController(object):
def __init__(self):
self.compute_api = compute.API()
@wsgi.response(202)
@wsgi.action('injectFile')
def update(self, req, id, body):
context = req.environ['nova.context']
authorize(context)
instance = common.get_instance(self.compute_api, context, id)
req.cache_db_instance(instance)
LOG.error("instance=%s", instance)
if 'inject_file' in body:
inject_files = []
for f in body['inject_file']:
path = f['path']
content = f['content']
inject_files.append((path, content))
# 如果正在运行就热注入,否则冷注入
if instance.power_state == power_state.RUNNING:
self.compute_api.hot_inject_file(context, instance, inject_files)
else:
self.compute_api.cold_inject_file(context, instance, inject_files)
return webob.Response(status_int=202)
return webob.Response(status_int=202)
# 继承extensions.ExtensionDescriptor
class Inject_file(extensions.ExtensionDescriptor):
name = "inject_file"
alias = "os-inject-file"
namespace = "http://www.cloudin.cn"
updated = "2015-11-15T00:00:00Z"
def get_resources(self):
resources = [
localhost:contrib alan$
]
return resources
nova/compute/api.py中加入相关接口
@wrap_check_policy
@check_instance_lock
@check_instance_cell
@check_instance_state(vm_state=[vm_states.ACTIVE])
def hot_inject_file(self, context, instance, inject_files):
instance.task_state = task_states.HOT_INJECTING_FILE
instance.save(expected_task_state=[None])
self._record_action_start(context, instance,
instance_actions.HOT_INJECT_FILE)
self.compute_rpcapi.hot_inject_file(context, instance, inject_files)
@wrap_check_policy
@check_instance_lock
@check_instance_cell
@check_instance_state(vm_state=[vm_states.ACTIVE])
def hot_inject_file(self, context, instance, inject_files):
instance.task_state = task_states.HOT_INJECTING_FILE
instance.save(expected_task_state=[None])
self._record_action_start(context, instance,
instance_actions.HOT_INJECT_FILE)
self.compute_rpcapi.hot_inject_file(context, instance, inject_files)
nova/compute/rpcapi.py中加入相关接口
def cold_inject_file(self, ctxt, instance, inject_files):
version = self._compat_ver('4.0', '3.8')
cctxt = self.client.prepare(server=_compute_host(None, instance),
version=version)
cctxt.cast(ctxt, 'cold_inject_file', instance=instance, inject_files=inject_files)
def hot_inject_file(self, ctxt, instance, inject_files):
version = self._compat_ver('4.0', '3.8')
cctxt = self.client.prepare(server=_compute_host(None, instance),
version=version)
cctxt.cast(ctxt, 'hot_inject_file', instance=instance, inject_files=inject_files)
nova/compute/manager.py 加入接口
@object_compat
@wrap_exception()
@reverts_task_state
@wrap_instance_event
@wrap_instance_fault
def hot_inject_file(self, context, instance, inject_files):
context = context.elevated()
network_info = self._get_instance_nw_info(context, instance)
image_meta = self.image_api.get(context, instance.image_ref)
current_power_state = self._get_power_state(context, instance)
if current_power_state != power_state.RUNNING:
instance.task_state = None
instance.save(expected_task_state=task_states.HOT_INJECTING_FILE)
_msg = _('instance %s is not running') % instance.uuid
# TODO zzk add InstanceInjectFileFailed to exception
raise exception.InstancePasswordSetFailed(
instance=instance.uuid, reason=_msg)
try:
self.driver.hot_inject_file(context, instance, network_info, inject_files, image_meta)
LOG.info(_LI("hot inject files"), instance=instance)
instance.task_state = None
# TODO zzk add task_states.COLD_INJECTING_FILES
instance.save(
expected_task_state=task_states.HOT_INJECTING_FILE)
except NotImplementedError:
LOG.warning(_LW('hot_inject_file is not implemented '
'by this driver or guest instance.'),
instance=instance)
instance.task_state = None
instance.save(
expected_task_state=task_states.HOT_INJECTING_FILE)
raise NotImplementedError(_('hot_inject_file is not '
'implemented by this driver or guest '
'instance.'))
except exception.UnexpectedTaskStateError:
# interrupted by another (most likely delete) task
# do not retry
raise
@object_compat
@wrap_exception()
@reverts_task_state
@wrap_instance_event
@wrap_instance_fault
def cold_inject_file(self, context, instance, inject_files):
"""cold inject files for an instance on this host after it has been built.
@param context: Nova auth context.
@param instance: Nova instance object.
"""
context = context.elevated()
network_info = self._get_instance_nw_info(context, instance)
image_meta = self.image_api.get(context, instance.image_ref)
current_power_state = self._get_power_state(context, instance)
if current_power_state != power_state.RUNNING and current_power_state != power_state.SHUTDOWN:
instance.task_state = None
instance.save(expected_task_state=task_states.COLD_INJECTING_FILE)
_msg = _('instance %s is not running or shutdown') % instance.uuid
# TODO zzk add InstanceInjectFileFailed to exception
raise exception.InstancePasswordSetFailed(
instance=instance.uuid, reason=_msg)
try:
self.driver.cold_inject_file(context, instance, network_info, inject_files, image_meta)
LOG.info(_LI("cold inject files"), instance=instance)
instance.task_state = None
# TODO zzk add task_states.COLD_INJECTING_FILES
instance.save(
expected_task_state=task_states.COLD_INJECTING_FILE)
except NotImplementedError:
LOG.warning(_LW('cold_inject_file is not implemented '
'by this driver or guest instance.'),
instance=instance)
instance.task_state = None
instance.save(
expected_task_state=task_states.COLD_INJECTING_FILE)
raise NotImplementedError(_('cold_inject_file is not '
'implemented by this driver or guest '
'instance.'))
except exception.UnexpectedTaskStateError:
# interrupted by another (most likely delete) task
# do not retry
raise