Move logic from os-api-host into compute
Renames host to host_name in various places to make it explicit what it is the APIs are accepting and what we are passing around. Moves logic out of the openstack api's host extension to the compute library. This is in preparation for cells; cells provides its own compute library that proxies certain calls to child cells. In an effort to separate out the layers of code, we're moving all DB related logic out of the openstack api layer and into compute library layer. For example @check_host which calls the DB should not be in the openstack api layer. In the case of cells, we can't check for the existence of the host until we arrive at the cell that contains the host. Implements: blueprint host-api-prep-for-cells Change-Id: Icb861fd323ba707ab3b661f0a2c6d8bb7bfdc85e
This commit is contained in:
parent
b35f866511
commit
745335bc3c
@ -22,9 +22,7 @@ from xml.parsers import expat
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.api.openstack import xmlutil
|
||||
from nova import availability_zones
|
||||
from nova.compute import api as compute_api
|
||||
from nova import db
|
||||
from nova import compute
|
||||
from nova import exception
|
||||
from nova.openstack.common import log as logging
|
||||
|
||||
@ -94,140 +92,162 @@ class HostUpdateDeserializer(wsgi.XMLDeserializer):
|
||||
return dict(body=updates)
|
||||
|
||||
|
||||
def _list_hosts(req):
|
||||
"""Returns a summary list of hosts, optionally filtering
|
||||
by service type.
|
||||
"""
|
||||
context = req.environ['nova.context']
|
||||
services = db.service_get_all(context, False)
|
||||
services = availability_zones.set_availability_zones(context, services)
|
||||
zone = ''
|
||||
if 'zone' in req.GET:
|
||||
zone = req.GET['zone']
|
||||
if zone:
|
||||
services = [s for s in services if s['availability_zone'] == zone]
|
||||
hosts = []
|
||||
for host in services:
|
||||
hosts.append({"host_name": host['host'], 'service': host['topic'],
|
||||
'zone': host['availability_zone']})
|
||||
return hosts
|
||||
|
||||
|
||||
def check_host(fn):
|
||||
"""Makes sure that the host exists."""
|
||||
def wrapped(self, req, id, *args, **kwargs):
|
||||
listed_hosts = _list_hosts(req)
|
||||
hosts = [h["host_name"] for h in listed_hosts]
|
||||
if id in hosts:
|
||||
return fn(self, req, id, *args, **kwargs)
|
||||
else:
|
||||
message = _("Host '%s' could not be found.") % id
|
||||
raise webob.exc.HTTPNotFound(explanation=message)
|
||||
return wrapped
|
||||
|
||||
|
||||
class HostController(object):
|
||||
"""The Hosts API controller for the OpenStack API."""
|
||||
def __init__(self):
|
||||
self.api = compute_api.HostAPI()
|
||||
self.api = compute.HostAPI()
|
||||
super(HostController, self).__init__()
|
||||
|
||||
@wsgi.serializers(xml=HostIndexTemplate)
|
||||
def index(self, req):
|
||||
authorize(req.environ['nova.context'])
|
||||
return {'hosts': _list_hosts(req)}
|
||||
"""
|
||||
:returns: A dict in the format:
|
||||
|
||||
{'hosts': [{'host_name': 'some.host.name',
|
||||
'service': 'cells'},
|
||||
{'host_name': 'some.other.host.name',
|
||||
'service': 'cells'},
|
||||
{'host_name': 'some.celly.host.name',
|
||||
'service': 'cells'},
|
||||
{'host_name': 'console1.host.com',
|
||||
'service': 'consoleauth'},
|
||||
{'host_name': 'network1.host.com',
|
||||
'service': 'network'},
|
||||
{'host_name': 'netwwork2.host.com',
|
||||
'service': 'network'},
|
||||
{'host_name': 'sched1.host.com',
|
||||
'service': 'scheduler'},
|
||||
{'host_name': 'sched2.host.com',
|
||||
'service': 'scheduler'},
|
||||
{'host_name': 'vol1.host.com',
|
||||
'service': 'volume'}]}
|
||||
"""
|
||||
context = req.environ['nova.context']
|
||||
authorize(context)
|
||||
zone = req.GET.get('zone', None)
|
||||
data = self.api.list_hosts(context, zone)
|
||||
|
||||
return {'hosts': data}
|
||||
|
||||
@wsgi.serializers(xml=HostUpdateTemplate)
|
||||
@wsgi.deserializers(xml=HostUpdateDeserializer)
|
||||
@check_host
|
||||
def update(self, req, id, body):
|
||||
authorize(req.environ['nova.context'])
|
||||
update_values = {}
|
||||
for raw_key, raw_val in body.iteritems():
|
||||
key = raw_key.lower().strip()
|
||||
val = raw_val.lower().strip()
|
||||
if key == "status":
|
||||
if val in ("enable", "disable"):
|
||||
update_values['status'] = val.startswith("enable")
|
||||
else:
|
||||
explanation = _("Invalid status: '%s'") % raw_val
|
||||
raise webob.exc.HTTPBadRequest(explanation=explanation)
|
||||
elif key == "maintenance_mode":
|
||||
if val not in ['enable', 'disable']:
|
||||
explanation = _("Invalid mode: '%s'") % raw_val
|
||||
raise webob.exc.HTTPBadRequest(explanation=explanation)
|
||||
update_values['maintenance_mode'] = val == 'enable'
|
||||
"""
|
||||
:param body: example format {'status': 'enable',
|
||||
'maintenance_mode': 'enable'}
|
||||
:returns:
|
||||
"""
|
||||
def read_enabled(orig_val, msg):
|
||||
"""
|
||||
:param orig_val: A string with either 'enable' or 'disable'. May
|
||||
be surrounded by whitespace, and case doesn't
|
||||
matter
|
||||
:param msg: The message to be passed to HTTPBadRequest. A single
|
||||
%s will be replaced with orig_val.
|
||||
:returns: True for 'enabled' and False for 'disabled'
|
||||
"""
|
||||
val = orig_val.strip().lower()
|
||||
if val == "enable":
|
||||
return True
|
||||
elif val == "disable":
|
||||
return False
|
||||
else:
|
||||
explanation = _("Invalid update setting: '%s'") % raw_key
|
||||
raise webob.exc.HTTPBadRequest(explanation=explanation)
|
||||
|
||||
# this is for handling multiple settings at the same time:
|
||||
# the result dictionaries are merged in the first one.
|
||||
# Note: the 'host' key will always be the same so it's
|
||||
# okay that it gets overwritten.
|
||||
update_setters = {'status': self._set_enabled_status,
|
||||
'maintenance_mode': self._set_host_maintenance}
|
||||
result = {}
|
||||
for key, value in update_values.iteritems():
|
||||
result.update(update_setters[key](req, id, value))
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg % orig_val)
|
||||
context = req.environ['nova.context']
|
||||
authorize(context)
|
||||
# See what the user wants to 'update'
|
||||
params = dict([(k.strip().lower(), v) for k, v in body.iteritems()])
|
||||
orig_status = status = params.pop('status', None)
|
||||
orig_maint_mode = maint_mode = params.pop('maintenance_mode', None)
|
||||
# Validate the request
|
||||
if len(params) > 0:
|
||||
# Some extra param was passed. Fail.
|
||||
explanation = _("Invalid update setting: '%s'") % params.keys()[0]
|
||||
raise webob.exc.HTTPBadRequest(explanation=explanation)
|
||||
if orig_status is not None:
|
||||
status = read_enabled(orig_status, _("Invalid status: '%s'"))
|
||||
if orig_maint_mode is not None:
|
||||
maint_mode = read_enabled(orig_maint_mode, _("Invalid mode: '%s'"))
|
||||
if status is None and maint_mode is None:
|
||||
explanation = _("'status' or 'maintenance_mode' needed for "
|
||||
"host update")
|
||||
raise webob.exc.HTTPBadRequest(explanation=explanation)
|
||||
# Make the calls and merge the results
|
||||
result = {'host': id}
|
||||
if status is not None:
|
||||
result['status'] = self._set_enabled_status(context, id, status)
|
||||
if maint_mode is not None:
|
||||
result['maintenance_mode'] = self._set_host_maintenance(context,
|
||||
id, maint_mode)
|
||||
return result
|
||||
|
||||
def _set_host_maintenance(self, req, host, mode=True):
|
||||
def _set_host_maintenance(self, context, host_name, mode=True):
|
||||
"""Start/Stop host maintenance window. On start, it triggers
|
||||
guest VMs evacuation."""
|
||||
context = req.environ['nova.context']
|
||||
LOG.audit(_("Putting host %(host)s in maintenance "
|
||||
LOG.audit(_("Putting host %(host_name)s in maintenance "
|
||||
"mode %(mode)s.") % locals())
|
||||
try:
|
||||
result = self.api.set_host_maintenance(context, host, mode)
|
||||
result = self.api.set_host_maintenance(context, host_name, mode)
|
||||
except NotImplementedError:
|
||||
msg = _("Virt driver does not implement host maintenance mode.")
|
||||
raise webob.exc.HTTPNotImplemented(explanation=msg)
|
||||
return {"host": host, "maintenance_mode": result}
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.message)
|
||||
if result not in ("on_maintenance", "off_maintenance"):
|
||||
raise webob.exc.HTTPBadRequest(explanation=result)
|
||||
return result
|
||||
|
||||
def _set_enabled_status(self, req, host, enabled):
|
||||
"""Sets the specified host's ability to accept new instances."""
|
||||
context = req.environ['nova.context']
|
||||
state = "enabled" if enabled else "disabled"
|
||||
LOG.audit(_("Setting host %(host)s to %(state)s.") % locals())
|
||||
def _set_enabled_status(self, context, host_name, enabled):
|
||||
"""Sets the specified host's ability to accept new instances.
|
||||
:param enabled: a boolean - if False no new VMs will be able to start
|
||||
on the host"""
|
||||
if enabled:
|
||||
LOG.audit(_("Enabling host %s.") % host_name)
|
||||
else:
|
||||
LOG.audit(_("Disabling host %s.") % host_name)
|
||||
try:
|
||||
result = self.api.set_host_enabled(context, host=host,
|
||||
enabled=enabled)
|
||||
result = self.api.set_host_enabled(context, host_name=host_name,
|
||||
enabled=enabled)
|
||||
except NotImplementedError:
|
||||
msg = _("Virt driver does not implement host disabled status.")
|
||||
raise webob.exc.HTTPNotImplemented(explanation=msg)
|
||||
return {"host": host, "status": result}
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.message)
|
||||
if result not in ("enabled", "disabled"):
|
||||
raise webob.exc.HTTPBadRequest(explanation=result)
|
||||
return result
|
||||
|
||||
def _host_power_action(self, req, host, action):
|
||||
def _host_power_action(self, req, host_name, action):
|
||||
"""Reboots, shuts down or powers up the host."""
|
||||
context = req.environ['nova.context']
|
||||
authorize(context)
|
||||
try:
|
||||
result = self.api.host_power_action(context, host=host,
|
||||
result = self.api.host_power_action(context, host_name=host_name,
|
||||
action=action)
|
||||
except NotImplementedError:
|
||||
msg = _("Virt driver does not implement host power management.")
|
||||
raise webob.exc.HTTPNotImplemented(explanation=msg)
|
||||
return {"host": host, "power_action": result}
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.message)
|
||||
return {"host": host_name, "power_action": result}
|
||||
|
||||
@wsgi.serializers(xml=HostActionTemplate)
|
||||
def startup(self, req, id):
|
||||
return self._host_power_action(req, host=id, action="startup")
|
||||
return self._host_power_action(req, host_name=id, action="startup")
|
||||
|
||||
@wsgi.serializers(xml=HostActionTemplate)
|
||||
def shutdown(self, req, id):
|
||||
return self._host_power_action(req, host=id, action="shutdown")
|
||||
return self._host_power_action(req, host_name=id, action="shutdown")
|
||||
|
||||
@wsgi.serializers(xml=HostActionTemplate)
|
||||
def reboot(self, req, id):
|
||||
return self._host_power_action(req, host=id, action="reboot")
|
||||
return self._host_power_action(req, host_name=id, action="reboot")
|
||||
|
||||
@wsgi.serializers(xml=HostShowTemplate)
|
||||
def show(self, req, id):
|
||||
"""Shows the physical/usage resource given by hosts.
|
||||
|
||||
:param context: security context
|
||||
:param host: hostname
|
||||
:param id: hostname
|
||||
:returns: expected to use HostShowTemplate.
|
||||
ex.::
|
||||
|
||||
@ -235,66 +255,15 @@ class HostController(object):
|
||||
D: {'host': 'hostname','project': 'admin',
|
||||
'cpu': 1, 'memory_mb': 2048, 'disk_gb': 30}
|
||||
"""
|
||||
host = id
|
||||
context = req.environ['nova.context']
|
||||
if not context.is_admin:
|
||||
try:
|
||||
data = self.api.describe_host(context, id)
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=e.message)
|
||||
except exception.AdminRequired:
|
||||
msg = _("Describe-resource is admin only functionality")
|
||||
raise webob.exc.HTTPForbidden(explanation=msg)
|
||||
|
||||
# Getting compute node info and related instances info
|
||||
try:
|
||||
compute_ref = db.service_get_all_compute_by_host(context, host)
|
||||
compute_ref = compute_ref[0]
|
||||
except exception.ComputeHostNotFound:
|
||||
raise webob.exc.HTTPNotFound(explanation=_("Host not found"))
|
||||
instance_refs = db.instance_get_all_by_host(context,
|
||||
compute_ref['host'])
|
||||
|
||||
# Getting total available/used resource
|
||||
compute_ref = compute_ref['compute_node'][0]
|
||||
resources = [{'resource': {'host': host, 'project': '(total)',
|
||||
'cpu': compute_ref['vcpus'],
|
||||
'memory_mb': compute_ref['memory_mb'],
|
||||
'disk_gb': compute_ref['local_gb']}},
|
||||
{'resource': {'host': host, 'project': '(used_now)',
|
||||
'cpu': compute_ref['vcpus_used'],
|
||||
'memory_mb': compute_ref['memory_mb_used'],
|
||||
'disk_gb': compute_ref['local_gb_used']}}]
|
||||
|
||||
cpu_sum = 0
|
||||
mem_sum = 0
|
||||
hdd_sum = 0
|
||||
for i in instance_refs:
|
||||
cpu_sum += i['vcpus']
|
||||
mem_sum += i['memory_mb']
|
||||
hdd_sum += i['root_gb'] + i['ephemeral_gb']
|
||||
|
||||
resources.append({'resource': {'host': host,
|
||||
'project': '(used_max)',
|
||||
'cpu': cpu_sum,
|
||||
'memory_mb': mem_sum,
|
||||
'disk_gb': hdd_sum}})
|
||||
|
||||
# Getting usage resource per project
|
||||
project_ids = [i['project_id'] for i in instance_refs]
|
||||
project_ids = list(set(project_ids))
|
||||
for project_id in project_ids:
|
||||
vcpus = [i['vcpus'] for i in instance_refs
|
||||
if i['project_id'] == project_id]
|
||||
|
||||
mem = [i['memory_mb'] for i in instance_refs
|
||||
if i['project_id'] == project_id]
|
||||
|
||||
disk = [i['root_gb'] + i['ephemeral_gb'] for i in instance_refs
|
||||
if i['project_id'] == project_id]
|
||||
|
||||
resources.append({'resource': {'host': host,
|
||||
'project': project_id,
|
||||
'cpu': reduce(lambda x, y: x + y, vcpus),
|
||||
'memory_mb': reduce(lambda x, y: x + y, mem),
|
||||
'disk_gb': reduce(lambda x, y: x + y, disk)}})
|
||||
|
||||
return {'host': resources}
|
||||
return {'host': data}
|
||||
|
||||
|
||||
class Hosts(extensions.ExtensionDescriptor):
|
||||
|
@ -33,6 +33,17 @@ nova.openstack.common.cfg.CONF.register_opts(_compute_opts)
|
||||
|
||||
def API(*args, **kwargs):
|
||||
importutils = nova.openstack.common.importutils
|
||||
compute_api_class = nova.openstack.common.cfg.CONF.compute_api_class
|
||||
cls = importutils.import_class(compute_api_class)
|
||||
return cls(*args, **kwargs)
|
||||
class_name = nova.openstack.common.cfg.CONF.compute_api_class
|
||||
return importutils.import_object(class_name, *args, **kwargs)
|
||||
|
||||
|
||||
def HostAPI(*args, **kwargs):
|
||||
"""
|
||||
Returns the 'HostAPI' class from the same module as the configured compute
|
||||
api
|
||||
"""
|
||||
importutils = nova.openstack.common.importutils
|
||||
compute_api_class_name = nova.openstack.common.cfg.CONF.compute_api_class
|
||||
compute_api_class = importutils.import_class(compute_api_class_name)
|
||||
class_name = compute_api_class.__module__ + ".HostAPI"
|
||||
return importutils.import_object(class_name, *args, **kwargs)
|
||||
|
@ -29,6 +29,7 @@ import time
|
||||
import urllib
|
||||
import uuid
|
||||
|
||||
from nova import availability_zones
|
||||
from nova import block_device
|
||||
from nova.compute import instance_types
|
||||
from nova.compute import power_state
|
||||
@ -150,7 +151,7 @@ def policy_decorator(scope):
|
||||
|
||||
wrap_check_policy = policy_decorator(scope='compute')
|
||||
wrap_check_security_groups_policy = policy_decorator(
|
||||
scope='compute:security_groups')
|
||||
scope='compute:security_groups')
|
||||
|
||||
|
||||
def check_policy(context, action, target, scope='compute'):
|
||||
@ -844,10 +845,10 @@ class API(base.Base):
|
||||
def trigger_provider_fw_rules_refresh(self, context):
|
||||
"""Called when a rule is added/removed from a provider firewall."""
|
||||
|
||||
hosts = [x['host'] for (x, idx)
|
||||
in self.db.service_get_all_compute_sorted(context)]
|
||||
for host in hosts:
|
||||
self.compute_rpcapi.refresh_provider_fw_rules(context, host)
|
||||
host_names = [x['host'] for (x, idx)
|
||||
in self.db.service_get_all_compute_sorted(context)]
|
||||
for host_name in host_names:
|
||||
self.compute_rpcapi.refresh_provider_fw_rules(context, host_name)
|
||||
|
||||
@wrap_check_policy
|
||||
def update(self, context, instance, **kwargs):
|
||||
@ -944,13 +945,14 @@ class API(base.Base):
|
||||
host=src_host, cast=False,
|
||||
reservations=downsize_reservations)
|
||||
|
||||
is_up = False
|
||||
# NOTE(jogo): db allows for multiple compute services per host
|
||||
try:
|
||||
services = self.db.service_get_all_compute_by_host(
|
||||
context.elevated(), instance['host'])
|
||||
except exception.ComputeHostNotFound:
|
||||
services = []
|
||||
|
||||
is_up = False
|
||||
for service in services:
|
||||
if self.servicegroup_api.service_is_up(service):
|
||||
is_up = True
|
||||
@ -1865,9 +1867,9 @@ class API(base.Base):
|
||||
"""Retrieve diagnostics for the given instance."""
|
||||
return self.compute_rpcapi.get_diagnostics(context, instance=instance)
|
||||
|
||||
def get_backdoor_port(self, context, host):
|
||||
def get_backdoor_port(self, context, host_name):
|
||||
"""Retrieve backdoor port."""
|
||||
return self.compute_rpcapi.get_backdoor_port(context, host)
|
||||
return self.compute_rpcapi.get_backdoor_port(context, host_name)
|
||||
|
||||
@wrap_check_policy
|
||||
@check_instance_lock
|
||||
@ -2133,45 +2135,148 @@ class API(base.Base):
|
||||
|
||||
@check_instance_state(vm_state=[vm_states.ACTIVE])
|
||||
def live_migrate(self, context, instance, block_migration,
|
||||
disk_over_commit, host):
|
||||
disk_over_commit, host_name):
|
||||
"""Migrate a server lively to a new host."""
|
||||
LOG.debug(_("Going to try to live migrate instance to %s"),
|
||||
host, instance=instance)
|
||||
host_name, instance=instance)
|
||||
|
||||
instance = self.update(context, instance,
|
||||
task_state=task_states.MIGRATING,
|
||||
expected_task_state=None)
|
||||
|
||||
self.scheduler_rpcapi.live_migration(context, block_migration,
|
||||
disk_over_commit, instance, host)
|
||||
disk_over_commit, instance, host_name)
|
||||
|
||||
|
||||
def check_host(fn):
|
||||
"""Decorator that makes sure that the host exists."""
|
||||
def wrapped(self, context, host_name, *args, **kwargs):
|
||||
if self.does_host_exist(context, host_name):
|
||||
return fn(self, context, host_name, *args, **kwargs)
|
||||
else:
|
||||
raise exception.HostNotFound(host=host_name)
|
||||
return wrapped
|
||||
|
||||
|
||||
class HostAPI(base.Base):
|
||||
"""Sub-set of the Compute Manager API for managing host operations."""
|
||||
|
||||
def __init__(self):
|
||||
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
|
||||
super(HostAPI, self).__init__()
|
||||
|
||||
"""Sub-set of the Compute Manager API for managing host operations."""
|
||||
def set_host_enabled(self, context, host, enabled):
|
||||
@check_host
|
||||
def set_host_enabled(self, context, host_name, enabled):
|
||||
"""Sets the specified host's ability to accept new instances."""
|
||||
# NOTE(comstud): No instance_uuid argument to this compute manager
|
||||
# call
|
||||
return self.compute_rpcapi.set_host_enabled(context, enabled=enabled,
|
||||
host=host)
|
||||
host=host_name)
|
||||
|
||||
def get_host_uptime(self, context, host):
|
||||
@check_host
|
||||
def get_host_uptime(self, context, host_name):
|
||||
"""Returns the result of calling "uptime" on the target host."""
|
||||
# NOTE(comstud): No instance_uuid argument to this compute manager
|
||||
# call
|
||||
return self.compute_rpcapi.get_host_uptime(context, host=host)
|
||||
return self.compute_rpcapi.get_host_uptime(context, host=host_name)
|
||||
|
||||
def host_power_action(self, context, host, action):
|
||||
@check_host
|
||||
def host_power_action(self, context, host_name, action):
|
||||
"""Reboots, shuts down or powers up the host."""
|
||||
# NOTE(comstud): No instance_uuid argument to this compute manager
|
||||
# call
|
||||
return self.compute_rpcapi.host_power_action(context, action=action,
|
||||
host=host)
|
||||
host=host_name)
|
||||
|
||||
def list_hosts(self, context, zone=None, service=None):
|
||||
"""Returns a summary list of enabled hosts, optionally filtering
|
||||
by zone and/or service type.
|
||||
"""
|
||||
LOG.debug(_("Listing hosts"))
|
||||
services = self.db.service_get_all(context, False)
|
||||
services = availability_zones.set_availability_zones(context, services)
|
||||
if zone:
|
||||
services = [s for s in services if s['availability_zone'] == zone]
|
||||
hosts = []
|
||||
for host in services:
|
||||
hosts.append({'host_name': host['host'], 'service': host['topic'],
|
||||
'zone': host['availability_zone']})
|
||||
if service:
|
||||
hosts = [host for host in hosts
|
||||
if host["service"] == service]
|
||||
return hosts
|
||||
|
||||
def does_host_exist(self, context, host_name):
|
||||
"""
|
||||
Returns True if the host with host_name exists, False otherwise
|
||||
"""
|
||||
return self.db.service_does_host_exist(context, host_name)
|
||||
|
||||
def describe_host(self, context, host_name):
|
||||
"""
|
||||
Returns information about a host in this kind of format:
|
||||
:returns:
|
||||
ex.::
|
||||
{'host': 'hostname',
|
||||
'project': 'admin',
|
||||
'cpu': 1,
|
||||
'memory_mb': 2048,
|
||||
'disk_gb': 30}
|
||||
"""
|
||||
# Getting compute node info and related instances info
|
||||
try:
|
||||
compute_ref = self.db.service_get_all_compute_by_host(context,
|
||||
host_name)
|
||||
compute_ref = compute_ref[0]
|
||||
except exception.ComputeHostNotFound:
|
||||
raise exception.HostNotFound(host=host_name)
|
||||
instance_refs = self.db.instance_get_all_by_host(context,
|
||||
compute_ref['host'])
|
||||
|
||||
# Getting total available/used resource
|
||||
compute_ref = compute_ref['compute_node'][0]
|
||||
resources = [{'resource': {'host': host_name, 'project': '(total)',
|
||||
'cpu': compute_ref['vcpus'],
|
||||
'memory_mb': compute_ref['memory_mb'],
|
||||
'disk_gb': compute_ref['local_gb']}},
|
||||
{'resource': {'host': host_name, 'project': '(used_now)',
|
||||
'cpu': compute_ref['vcpus_used'],
|
||||
'memory_mb': compute_ref['memory_mb_used'],
|
||||
'disk_gb': compute_ref['local_gb_used']}}]
|
||||
|
||||
cpu_sum = 0
|
||||
mem_sum = 0
|
||||
hdd_sum = 0
|
||||
for i in instance_refs:
|
||||
cpu_sum += i['vcpus']
|
||||
mem_sum += i['memory_mb']
|
||||
hdd_sum += i['root_gb'] + i['ephemeral_gb']
|
||||
|
||||
resources.append({'resource': {'host': host_name,
|
||||
'project': '(used_max)',
|
||||
'cpu': cpu_sum,
|
||||
'memory_mb': mem_sum,
|
||||
'disk_gb': hdd_sum}})
|
||||
|
||||
# Getting usage resource per project
|
||||
project_ids = [i['project_id'] for i in instance_refs]
|
||||
project_ids = list(set(project_ids))
|
||||
for project_id in project_ids:
|
||||
vcpus = [i['vcpus'] for i in instance_refs
|
||||
if i['project_id'] == project_id]
|
||||
|
||||
mem = [i['memory_mb'] for i in instance_refs
|
||||
if i['project_id'] == project_id]
|
||||
|
||||
disk = [i['root_gb'] + i['ephemeral_gb'] for i in instance_refs
|
||||
if i['project_id'] == project_id]
|
||||
|
||||
resources.append({'resource': {'host': host_name,
|
||||
'project': project_id,
|
||||
'cpu': sum(vcpus),
|
||||
'memory_mb': sum(mem),
|
||||
'disk_gb': sum(disk)}})
|
||||
return resources
|
||||
|
||||
@check_host
|
||||
def set_host_maintenance(self, context, host, mode):
|
||||
"""Start/Stop host maintenance window. On start, it triggers
|
||||
guest VMs evacuation."""
|
||||
@ -2237,25 +2342,27 @@ class AggregateAPI(base.Base):
|
||||
reason='not empty')
|
||||
self.db.aggregate_delete(context, aggregate_id)
|
||||
|
||||
def add_host_to_aggregate(self, context, aggregate_id, host):
|
||||
def add_host_to_aggregate(self, context, aggregate_id, host_name):
|
||||
"""Adds the host to an aggregate."""
|
||||
# validates the host; ComputeHostNotFound is raised if invalid
|
||||
service = self.db.service_get_all_compute_by_host(context, host)[0]
|
||||
service = self.db.service_get_all_compute_by_host(
|
||||
context, host_name)[0]
|
||||
aggregate = self.db.aggregate_get(context, aggregate_id)
|
||||
self.db.aggregate_host_add(context, aggregate_id, host)
|
||||
self.db.aggregate_host_add(context, aggregate_id, host_name)
|
||||
#NOTE(jogo): Send message to host to support resource pools
|
||||
self.compute_rpcapi.add_aggregate_host(context,
|
||||
aggregate=aggregate, host_param=host, host=host)
|
||||
aggregate=aggregate, host_param=host_name, host=host_name)
|
||||
return self.get_aggregate(context, aggregate_id)
|
||||
|
||||
def remove_host_from_aggregate(self, context, aggregate_id, host):
|
||||
def remove_host_from_aggregate(self, context, aggregate_id, host_name):
|
||||
"""Removes host from the aggregate."""
|
||||
# validates the host; ComputeHostNotFound is raised if invalid
|
||||
service = self.db.service_get_all_compute_by_host(context, host)[0]
|
||||
service = self.db.service_get_all_compute_by_host(
|
||||
context, host_name)[0]
|
||||
aggregate = self.db.aggregate_get(context, aggregate_id)
|
||||
self.db.aggregate_host_delete(context, aggregate_id, host)
|
||||
self.db.aggregate_host_delete(context, aggregate_id, host_name)
|
||||
self.compute_rpcapi.remove_aggregate_host(context,
|
||||
aggregate=aggregate, host_param=host, host=host)
|
||||
aggregate=aggregate, host_param=host_name, host=host_name)
|
||||
return self.get_aggregate(context, aggregate_id)
|
||||
|
||||
def _get_aggregate_info(self, context, aggregate):
|
||||
|
@ -132,6 +132,15 @@ def service_get_all(context, disabled=None):
|
||||
return IMPL.service_get_all(context, disabled)
|
||||
|
||||
|
||||
def service_does_host_exist(context, host_name, include_disabled=False):
|
||||
"""Returns True if 'host_name' is found in the services table, False
|
||||
otherwise
|
||||
:param: host_name - the name of the host we want to check if it exists
|
||||
:param: include_disabled - Set to True to include hosts from disabled
|
||||
services"""
|
||||
return IMPL.service_does_host_exist(context, host_name, include_disabled)
|
||||
|
||||
|
||||
def service_get_all_by_topic(context, topic):
|
||||
"""Get all services for a given topic."""
|
||||
return IMPL.service_get_all_by_topic(context, topic)
|
||||
|
@ -336,6 +336,15 @@ def service_get_all(context, disabled=None):
|
||||
return query.all()
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def service_does_host_exist(context, host_name, include_disabled):
|
||||
query = get_session().query(func.count(models.Service.host)).\
|
||||
filter_by(host=host_name)
|
||||
if not include_disabled:
|
||||
query = query.filter_by(disabled=False)
|
||||
return query.scalar() > 0
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def service_get_all_by_topic(context, topic):
|
||||
return model_query(context, models.Service, read_deleted="no").\
|
||||
|
@ -19,59 +19,68 @@ import webob.exc
|
||||
from nova.api.openstack.compute.contrib import hosts as os_hosts
|
||||
from nova.compute import power_state
|
||||
from nova.compute import vm_states
|
||||
from nova import context
|
||||
from nova import context as context_maker
|
||||
from nova import db
|
||||
from nova.openstack.common import log as logging
|
||||
from nova import test
|
||||
from nova.tests import fake_hosts
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
HOST_LIST = {"hosts": [
|
||||
{"host_name": "host_c1", "service": "compute", "zone": "nova"},
|
||||
{"host_name": "host_c2", "service": "compute", "zone": "nova"}]
|
||||
}
|
||||
HOST_LIST_NOVA_ZONE = [
|
||||
{"host_name": "host_c1", "service": "compute", "zone": "nova"},
|
||||
{"host_name": "host_c2", "service": "compute", "zone": "nova"}]
|
||||
SERVICES_LIST = [
|
||||
{"host": "host_c1", "topic": "compute"},
|
||||
{"host": "host_c2", "topic": "compute"}]
|
||||
|
||||
|
||||
def stub_service_get_all(self, req):
|
||||
return SERVICES_LIST
|
||||
def stub_service_get_all(context, disabled=None):
|
||||
return fake_hosts.SERVICES_LIST
|
||||
|
||||
|
||||
def stub_set_host_enabled(context, host, enabled):
|
||||
if host == "notimplemented":
|
||||
def stub_service_does_host_exist(context, host_name):
|
||||
return host_name in [row['host'] for row in stub_service_get_all(context)]
|
||||
|
||||
|
||||
def stub_set_host_enabled(context, host_name, enabled):
|
||||
"""
|
||||
Simulates three possible behaviours for VM drivers or compute drivers when
|
||||
enabling or disabling a host.
|
||||
|
||||
'enabled' means new instances can go to this host
|
||||
'disabled' means they can't
|
||||
"""
|
||||
results = {True: "enabled", False: "disabled"}
|
||||
if host_name == "notimplemented":
|
||||
# The vm driver for this host doesn't support this feature
|
||||
raise NotImplementedError()
|
||||
elif host_name == "host_c2":
|
||||
# Simulate a failure
|
||||
return results[not enabled]
|
||||
else:
|
||||
# Do the right thing
|
||||
return results[enabled]
|
||||
|
||||
|
||||
def stub_set_host_maintenance(context, host_name, mode):
|
||||
# We'll simulate success and failure by assuming
|
||||
# that 'host_c1' always succeeds, and 'host_c2'
|
||||
# always fails
|
||||
fail = (host == "host_c2")
|
||||
status = "enabled" if (enabled != fail) else "disabled"
|
||||
return status
|
||||
|
||||
|
||||
def stub_set_host_maintenance(context, host, mode):
|
||||
if host == "notimplemented":
|
||||
results = {True: "on_maintenance", False: "off_maintenance"}
|
||||
if host_name == "notimplemented":
|
||||
# The vm driver for this host doesn't support this feature
|
||||
raise NotImplementedError()
|
||||
# We'll simulate success and failure by assuming
|
||||
# that 'host_c1' always succeeds, and 'host_c2'
|
||||
# always fails
|
||||
fail = (host == "host_c2")
|
||||
maintenance = "on_maintenance" if (mode != fail) else "off_maintenance"
|
||||
return maintenance
|
||||
elif host_name == "host_c2":
|
||||
# Simulate a failure
|
||||
return results[not mode]
|
||||
else:
|
||||
# Do the right thing
|
||||
return results[mode]
|
||||
|
||||
|
||||
def stub_host_power_action(context, host, action):
|
||||
if host == "notimplemented":
|
||||
def stub_host_power_action(context, host_name, action):
|
||||
if host_name == "notimplemented":
|
||||
raise NotImplementedError()
|
||||
return action
|
||||
|
||||
|
||||
def _create_instance(**kwargs):
|
||||
"""Create a test instance."""
|
||||
ctxt = context.get_admin_context()
|
||||
ctxt = context_maker.get_admin_context()
|
||||
return db.instance_create(ctxt, _create_instance_dict(**kwargs))
|
||||
|
||||
|
||||
@ -99,12 +108,12 @@ def _create_instance_dict(**kwargs):
|
||||
|
||||
|
||||
class FakeRequest(object):
|
||||
environ = {"nova.context": context.get_admin_context()}
|
||||
environ = {"nova.context": context_maker.get_admin_context()}
|
||||
GET = {}
|
||||
|
||||
|
||||
class FakeRequestWithNovaZone(object):
|
||||
environ = {"nova.context": context.get_admin_context()}
|
||||
environ = {"nova.context": context_maker.get_admin_context()}
|
||||
GET = {"zone": "nova"}
|
||||
|
||||
|
||||
@ -114,14 +123,22 @@ class HostTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(HostTestCase, self).setUp()
|
||||
self.controller = os_hosts.HostController()
|
||||
self.hosts_api = self.controller.api
|
||||
self.req = FakeRequest()
|
||||
|
||||
# Pretend we have fake_hosts.HOST_LIST in the DB
|
||||
self.stubs.Set(db, 'service_get_all',
|
||||
stub_service_get_all)
|
||||
self.stubs.Set(self.controller.api, 'set_host_enabled',
|
||||
# Only hosts in our fake DB exist
|
||||
self.stubs.Set(db, 'service_does_host_exist',
|
||||
stub_service_does_host_exist)
|
||||
# 'host_c1' always succeeds, and 'host_c2'
|
||||
self.stubs.Set(self.hosts_api, 'set_host_enabled',
|
||||
stub_set_host_enabled)
|
||||
self.stubs.Set(self.controller.api, 'set_host_maintenance',
|
||||
# 'host_c1' always succeeds, and 'host_c2'
|
||||
self.stubs.Set(self.hosts_api, 'set_host_maintenance',
|
||||
stub_set_host_maintenance)
|
||||
self.stubs.Set(self.controller.api, 'host_power_action',
|
||||
self.stubs.Set(self.hosts_api, 'host_power_action',
|
||||
stub_host_power_action)
|
||||
|
||||
def _test_host_update(self, host, key, val, expected_value):
|
||||
@ -130,14 +147,17 @@ class HostTestCase(test.TestCase):
|
||||
self.assertEqual(result[key], expected_value)
|
||||
|
||||
def test_list_hosts(self):
|
||||
# Verify that the compute hosts are returned.
|
||||
hosts = os_hosts._list_hosts(self.req)
|
||||
self.assertEqual(hosts, HOST_LIST['hosts'])
|
||||
"""Verify that the compute hosts are returned."""
|
||||
result = self.controller.index(self.req)
|
||||
self.assert_('hosts' in result)
|
||||
hosts = result['hosts']
|
||||
self.assertEqual(fake_hosts.HOST_LIST, hosts)
|
||||
|
||||
def test_list_hosts_with_zone(self):
|
||||
req = FakeRequestWithNovaZone()
|
||||
hosts = os_hosts._list_hosts(req)
|
||||
self.assertEqual(hosts, HOST_LIST_NOVA_ZONE)
|
||||
result = self.controller.index(FakeRequestWithNovaZone())
|
||||
self.assert_('hosts' in result)
|
||||
hosts = result['hosts']
|
||||
self.assertEqual(fake_hosts.HOST_LIST_NOVA_ZONE, hosts)
|
||||
|
||||
def test_disable_host(self):
|
||||
self._test_host_update('host_c1', 'status', 'disable', 'disabled')
|
||||
@ -222,10 +242,6 @@ class HostTestCase(test.TestCase):
|
||||
self.assertEqual(result["status"], "disabled")
|
||||
self.assertEqual(result["maintenance_mode"], "on_maintenance")
|
||||
|
||||
def test_bad_host(self):
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.update,
|
||||
self.req, "bogus_host_name", {"status": "disable"})
|
||||
|
||||
def test_show_forbidden(self):
|
||||
self.req.environ["nova.context"].is_admin = False
|
||||
dest = 'dummydest'
|
||||
@ -244,7 +260,7 @@ class HostTestCase(test.TestCase):
|
||||
|
||||
def _create_compute_service(self):
|
||||
"""Create compute-manager(ComputeNode and Service record)."""
|
||||
ctxt = context.get_admin_context()
|
||||
ctxt = self.req.environ["nova.context"]
|
||||
dic = {'host': 'dummy', 'binary': 'nova-compute', 'topic': 'compute',
|
||||
'report_count': 0}
|
||||
s_ref = db.service_create(ctxt, dic)
|
||||
@ -259,8 +275,8 @@ class HostTestCase(test.TestCase):
|
||||
return db.service_get(ctxt, s_ref['id'])
|
||||
|
||||
def test_show_no_project(self):
|
||||
# No instance are running on the given host.
|
||||
ctxt = context.get_admin_context()
|
||||
"""No instances are running on the given host."""
|
||||
ctxt = context_maker.get_admin_context()
|
||||
s_ref = self._create_compute_service()
|
||||
|
||||
result = self.controller.show(self.req, s_ref['host'])
|
||||
@ -275,8 +291,8 @@ class HostTestCase(test.TestCase):
|
||||
db.service_destroy(ctxt, s_ref['id'])
|
||||
|
||||
def test_show_works_correctly(self):
|
||||
# show() works correctly as expected.
|
||||
ctxt = context.get_admin_context()
|
||||
"""show() works correctly as expected."""
|
||||
ctxt = context_maker.get_admin_context()
|
||||
s_ref = self._create_compute_service()
|
||||
i_ref1 = _create_instance(project_id='p-01', host=s_ref['host'])
|
||||
i_ref2 = _create_instance(project_id='p-02', vcpus=3,
|
||||
@ -303,17 +319,17 @@ class HostSerializerTest(test.TestCase):
|
||||
|
||||
def test_index_serializer(self):
|
||||
serializer = os_hosts.HostIndexTemplate()
|
||||
text = serializer.serialize(HOST_LIST)
|
||||
text = serializer.serialize(fake_hosts.OS_API_HOST_LIST)
|
||||
|
||||
tree = etree.fromstring(text)
|
||||
|
||||
self.assertEqual('hosts', tree.tag)
|
||||
self.assertEqual(len(HOST_LIST['hosts']), len(tree))
|
||||
for i in range(len(HOST_LIST)):
|
||||
self.assertEqual(len(fake_hosts.HOST_LIST), len(tree))
|
||||
for i in range(len(fake_hosts.HOST_LIST)):
|
||||
self.assertEqual('host', tree[i].tag)
|
||||
self.assertEqual(HOST_LIST['hosts'][i]['host_name'],
|
||||
self.assertEqual(fake_hosts.HOST_LIST[i]['host_name'],
|
||||
tree[i].get('host_name'))
|
||||
self.assertEqual(HOST_LIST['hosts'][i]['service'],
|
||||
self.assertEqual(fake_hosts.HOST_LIST[i]['service'],
|
||||
tree[i].get('service'))
|
||||
|
||||
def test_update_serializer_with_status(self):
|
||||
|
@ -5770,7 +5770,7 @@ class ComputeAPITestCase(BaseTestCase):
|
||||
self.compute_api.live_migrate(self.context, instance,
|
||||
block_migration=True,
|
||||
disk_over_commit=True,
|
||||
host='fake_dest_host')
|
||||
host_name='fake_dest_host')
|
||||
|
||||
instance = db.instance_get_by_uuid(self.context, instance_uuid)
|
||||
self.assertEqual(instance['task_state'], task_states.MIGRATING)
|
||||
@ -6066,7 +6066,7 @@ class ComputePolicyTestCase(BaseTestCase):
|
||||
|
||||
self.assertRaises(exception.PolicyNotAuthorized,
|
||||
self.compute_api.get_instance_faults,
|
||||
self.context, instances)
|
||||
context.get_admin_context(), instances)
|
||||
|
||||
def test_force_host_fail(self):
|
||||
rules = {"compute:create": [],
|
||||
@ -6098,11 +6098,19 @@ class ComputeHostAPITestCase(BaseTestCase):
|
||||
call_info['msg'] = msg
|
||||
self.stubs.Set(rpc, 'call', fake_rpc_call)
|
||||
|
||||
def _pretend_fake_host_exists(self, ctxt):
|
||||
"""Sets it so that the host API always thinks that 'fake_host'
|
||||
exists"""
|
||||
self.mox.StubOutWithMock(self.host_api, 'does_host_exist')
|
||||
self.host_api.does_host_exist(ctxt, 'fake_host').AndReturn(True)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
def test_set_host_enabled(self):
|
||||
ctxt = context.RequestContext('fake', 'fake')
|
||||
ctxt = context.get_admin_context()
|
||||
call_info = {}
|
||||
self._rpc_call_stub(call_info)
|
||||
|
||||
self._pretend_fake_host_exists(ctxt)
|
||||
self.host_api.set_host_enabled(ctxt, 'fake_host', 'fake_enabled')
|
||||
self.assertEqual(call_info['context'], ctxt)
|
||||
self.assertEqual(call_info['topic'], 'compute.fake_host')
|
||||
@ -6116,6 +6124,7 @@ class ComputeHostAPITestCase(BaseTestCase):
|
||||
call_info = {}
|
||||
self._rpc_call_stub(call_info)
|
||||
|
||||
self._pretend_fake_host_exists(ctxt)
|
||||
self.host_api.get_host_uptime(ctxt, 'fake_host')
|
||||
self.assertEqual(call_info['context'], ctxt)
|
||||
self.assertEqual(call_info['topic'], 'compute.fake_host')
|
||||
@ -6125,9 +6134,10 @@ class ComputeHostAPITestCase(BaseTestCase):
|
||||
'version': compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION})
|
||||
|
||||
def test_host_power_action(self):
|
||||
ctxt = context.RequestContext('fake', 'fake')
|
||||
ctxt = context.get_admin_context()
|
||||
call_info = {}
|
||||
self._rpc_call_stub(call_info)
|
||||
self._pretend_fake_host_exists(ctxt)
|
||||
self.host_api.host_power_action(ctxt, 'fake_host', 'fake_action')
|
||||
self.assertEqual(call_info['context'], ctxt)
|
||||
self.assertEqual(call_info['topic'], 'compute.fake_host')
|
||||
@ -6138,9 +6148,10 @@ class ComputeHostAPITestCase(BaseTestCase):
|
||||
compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION})
|
||||
|
||||
def test_set_host_maintenance(self):
|
||||
ctxt = context.RequestContext('fake', 'fake')
|
||||
ctxt = context.get_admin_context()
|
||||
call_info = {}
|
||||
self._rpc_call_stub(call_info)
|
||||
self._pretend_fake_host_exists(ctxt)
|
||||
self.host_api.set_host_maintenance(ctxt, 'fake_host', 'fake_mode')
|
||||
self.assertEqual(call_info['context'], ctxt)
|
||||
self.assertEqual(call_info['topic'], 'compute.fake_host')
|
||||
|
105
nova/tests/compute/test_host_api.py
Normal file
105
nova/tests/compute/test_host_api.py
Normal file
@ -0,0 +1,105 @@
|
||||
# Copyright (c) 2012 OpenStack, LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 nova.compute import api
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova import test
|
||||
from nova.tests import fake_hosts
|
||||
|
||||
|
||||
class HostApiTestCase(test.TestCase):
|
||||
"""
|
||||
Tests 'host' subset of the compute api
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(HostApiTestCase, self).setUp()
|
||||
self.compute_rpcapi = api.compute_rpcapi
|
||||
self.api = api.HostAPI()
|
||||
|
||||
def test_bad_host_set_enabled(self):
|
||||
"""
|
||||
Tests that actions on single hosts that don't exist blow up without
|
||||
having to reach the host via rpc. Should raise HostNotFound if you
|
||||
try to update a host that is not in the DB
|
||||
"""
|
||||
self.assertRaises(exception.HostNotFound, self.api.set_host_enabled,
|
||||
context.get_admin_context(), "bogus_host_name", False)
|
||||
|
||||
def test_list_compute_hosts(self):
|
||||
ctx = context.get_admin_context()
|
||||
self.mox.StubOutWithMock(db, 'service_get_all')
|
||||
db.service_get_all(ctx, False).AndReturn(fake_hosts.SERVICES_LIST)
|
||||
self.mox.ReplayAll()
|
||||
compute_hosts = self.api.list_hosts(ctx, service="compute")
|
||||
self.mox.VerifyAll()
|
||||
expected = [host for host in fake_hosts.HOST_LIST
|
||||
if host["service"] == "compute"]
|
||||
self.assertEqual(expected, compute_hosts)
|
||||
|
||||
def test_describe_host(self):
|
||||
"""
|
||||
Makes sure that describe_host returns the correct information
|
||||
given our fake input.
|
||||
"""
|
||||
ctx = context.get_admin_context()
|
||||
self.mox.StubOutWithMock(db, 'service_get_all_compute_by_host')
|
||||
host_name = 'host_c1'
|
||||
db.service_get_all_compute_by_host(ctx, host_name).AndReturn(
|
||||
[{'host': 'fake_host',
|
||||
'compute_node': [
|
||||
{'vcpus': 4,
|
||||
'vcpus_used': 1,
|
||||
'memory_mb': 8192,
|
||||
'memory_mb_used': 2048,
|
||||
'local_gb': 1024,
|
||||
'local_gb_used': 648}
|
||||
]
|
||||
}])
|
||||
self.mox.StubOutWithMock(db, 'instance_get_all_by_host')
|
||||
db.instance_get_all_by_host(ctx, 'fake_host').AndReturn(
|
||||
[{'project_id': 42,
|
||||
'vcpus': 1,
|
||||
'memory_mb': 2048,
|
||||
'root_gb': 648,
|
||||
'ephemeral_gb': 0,
|
||||
}])
|
||||
self.mox.ReplayAll()
|
||||
result = self.api.describe_host(ctx, host_name)
|
||||
self.assertEqual(result,
|
||||
[{'resource': {'cpu': 4,
|
||||
'disk_gb': 1024,
|
||||
'host': 'host_c1',
|
||||
'memory_mb': 8192,
|
||||
'project': '(total)'}},
|
||||
{'resource': {'cpu': 1,
|
||||
'disk_gb': 648,
|
||||
'host': 'host_c1',
|
||||
'memory_mb': 2048,
|
||||
'project': '(used_now)'}},
|
||||
{'resource': {'cpu': 1,
|
||||
'disk_gb': 648,
|
||||
'host': 'host_c1',
|
||||
'memory_mb': 2048,
|
||||
'project': '(used_max)'}},
|
||||
{'resource': {'cpu': 1,
|
||||
'disk_gb': 648,
|
||||
'host': 'host_c1',
|
||||
'memory_mb': 2048,
|
||||
'project': 42}}]
|
||||
)
|
||||
self.mox.VerifyAll()
|
32
nova/tests/fake_hosts.py
Normal file
32
nova/tests/fake_hosts.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright (c) 2012 OpenStack, LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Provides some fake hosts to test host and service related functions
|
||||
"""
|
||||
|
||||
HOST_LIST = [
|
||||
{"host_name": "host_c1", "service": "compute", "zone": "nova"},
|
||||
{"host_name": "host_c2", "service": "compute", "zone": "nova"}]
|
||||
|
||||
OS_API_HOST_LIST = {"hosts": HOST_LIST}
|
||||
|
||||
HOST_LIST_NOVA_ZONE = [
|
||||
{"host_name": "host_c1", "service": "compute", "zone": "nova"},
|
||||
{"host_name": "host_c2", "service": "compute", "zone": "nova"}]
|
||||
|
||||
SERVICES_LIST = [
|
||||
{"host": "host_c1", "topic": "compute"},
|
||||
{"host": "host_c2", "topic": "compute"}]
|
Loading…
x
Reference in New Issue
Block a user