Add host_status attribute for servers/detail and servers/{server_id}
When a compute service fails, the power states of the hosted VMs are not updated. A normal user querying his or her VMs does not get any indication about the failure. Also there is no indication about maintenance. This change will expose new attribute host_status to user querying his VMs. Attribute is only seen if policy allows. DocImpact: This adds API microversion Implements blueprint get-valid-server-state APIImpact Change-Id: I5abea08bdc27624a7f23a7db8964f8c2a7b0eaa7
This commit is contained in:
parent
98d0969254
commit
9345d5835f
@ -0,0 +1,69 @@
|
||||
{
|
||||
"server": {
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"addr": "192.168.0.3",
|
||||
"OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff",
|
||||
"OS-EXT-IPS:type": "fixed",
|
||||
"version": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "2013-09-16T02:55:07Z",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "3bf189131c61d0e71b0a8686a897a0f50d1693b48c47b721fe77155b",
|
||||
"id": "c278163e-36f9-4cf2-b1ac-80db4c63f7a8",
|
||||
"image": {
|
||||
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"key_name": null,
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2/openstack/servers/c278163e-36f9-4cf2-b1ac-80db4c63f7a8",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/servers/c278163e-36f9-4cf2-b1ac-80db4c63f7a8",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"name": "new-server-test",
|
||||
"OS-EXT-SRV-ATTR:host": "c5f474bf81474f9dbbc404d5b2e4e9b3",
|
||||
"OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini",
|
||||
"OS-EXT-SRV-ATTR:instance_name": "instance-00000001",
|
||||
"OS-EXT-SRV-ATTR:hostname": "new-server-test",
|
||||
"OS-EXT-SRV-ATTR:launch_index": 0,
|
||||
"OS-EXT-SRV-ATTR:reservation_id": "r-12345678",
|
||||
"OS-EXT-SRV-ATTR:root_device_name": "/dev/sda",
|
||||
"OS-EXT-SRV-ATTR:kernel_id": null,
|
||||
"OS-EXT-SRV-ATTR:ramdisk_id": null,
|
||||
"OS-EXT-SRV-ATTR:user_data": null,
|
||||
"locked": false,
|
||||
"accessIPv4": "1.2.3.4",
|
||||
"accessIPv6": "80fe::",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"host_status": "UP",
|
||||
"tenant_id": "openstack",
|
||||
"updated": "2013-09-16T02:55:08Z",
|
||||
"user_id": "fake"
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
{
|
||||
"servers": [
|
||||
{
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"addr": "192.168.0.3",
|
||||
"OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff",
|
||||
"OS-EXT-IPS:type": "fixed",
|
||||
"version": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "2013-09-16T02:55:03Z",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "63cf07a9fd82e1d2294926ec5c0d2e1e0ca449224246df75e16f23dc",
|
||||
"id": "a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46",
|
||||
"image": {
|
||||
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"key_name": null,
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2/openstack/servers/a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/servers/a8c1c13d-ec7e-47c7-b4ff-077f72c1ca46",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"name": "new-server-test",
|
||||
"OS-EXT-SRV-ATTR:host": "bc8efe4fdb7148a4bb921a2b03d17de6",
|
||||
"OS-EXT-SRV-ATTR:hypervisor_hostname": "fake-mini",
|
||||
"OS-EXT-SRV-ATTR:instance_name": "instance-00000001",
|
||||
"OS-EXT-SRV-ATTR:hostname": "new-server-test",
|
||||
"OS-EXT-SRV-ATTR:launch_index": 0,
|
||||
"OS-EXT-SRV-ATTR:reservation_id": "r-12345678",
|
||||
"OS-EXT-SRV-ATTR:root_device_name": "/dev/sda",
|
||||
"OS-EXT-SRV-ATTR:kernel_id": null,
|
||||
"OS-EXT-SRV-ATTR:ramdisk_id": null,
|
||||
"OS-EXT-SRV-ATTR:user_data": null,
|
||||
"locked": false,
|
||||
"accessIPv4": "1.2.3.4",
|
||||
"accessIPv6": "80fe::",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"host_status": "UP",
|
||||
"tenant_id": "openstack",
|
||||
"updated": "2013-09-16T02:55:05Z",
|
||||
"user_id": "fake"
|
||||
}
|
||||
]
|
||||
}
|
58
doc/api_samples/servers/v2.16/server-get-resp.json
Normal file
58
doc/api_samples/servers/v2.16/server-get-resp.json
Normal file
@ -0,0 +1,58 @@
|
||||
{
|
||||
"server": {
|
||||
"accessIPv4": "1.2.3.4",
|
||||
"accessIPv6": "80fe::",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"addr": "192.168.0.3",
|
||||
"OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff",
|
||||
"OS-EXT-IPS:type": "fixed",
|
||||
"version": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "2013-09-03T04:01:32Z",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "92154fab69d5883ba2c8622b7e65f745dd33257221c07af363c51b29",
|
||||
"id": "0e44cc9c-e052-415d-afbf-469b0d384170",
|
||||
"image": {
|
||||
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"key_name": null,
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2/openstack/servers/0e44cc9c-e052-415d-afbf-469b0d384170",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/servers/0e44cc9c-e052-415d-afbf-469b0d384170",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"name": "new-server-test",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"host_status": "UP",
|
||||
"tenant_id": "openstack",
|
||||
"updated": "2013-09-03T04:01:33Z",
|
||||
"user_id": "fake"
|
||||
}
|
||||
}
|
60
doc/api_samples/servers/v2.16/servers-details-resp.json
Normal file
60
doc/api_samples/servers/v2.16/servers-details-resp.json
Normal file
@ -0,0 +1,60 @@
|
||||
{
|
||||
"servers": [
|
||||
{
|
||||
"accessIPv4": "1.2.3.4",
|
||||
"accessIPv6": "80fe::",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"addr": "192.168.0.3",
|
||||
"OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff",
|
||||
"OS-EXT-IPS:type": "fixed",
|
||||
"version": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "2013-09-03T04:01:32Z",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "bcf92836fc9ed4203a75cb0337afc7f917d2be504164b995c2334b25",
|
||||
"id": "f5dc173b-6804-445a-a6d8-c705dad5b5eb",
|
||||
"image": {
|
||||
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"key_name": null,
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2/openstack/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://openstack.example.com/openstack/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"name": "new-server-test",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"host_status": "UP",
|
||||
"tenant_id": "openstack",
|
||||
"updated": "2013-09-03T04:01:32Z",
|
||||
"user_id": "fake"
|
||||
}
|
||||
]
|
||||
}
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.15",
|
||||
"version": "2.16",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.15",
|
||||
"version": "2.16",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -264,6 +264,7 @@
|
||||
"os_compute_api:servers:resize": "",
|
||||
"os_compute_api:servers:revert_resize": "",
|
||||
"os_compute_api:servers:show": "",
|
||||
"os_compute_api:servers:show:host_status": "rule:admin_api",
|
||||
"os_compute_api:servers:create_image": "",
|
||||
"os_compute_api:servers:create_image:allow_volume_backed": "",
|
||||
"os_compute_api:servers:start": "rule:admin_or_owner",
|
||||
|
@ -57,6 +57,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
* 2.14 - Remove onSharedStorage from evacuate request body and remove
|
||||
adminPass from the response body
|
||||
* 2.15 - Add soft-affinity and soft-anti-affinity policies
|
||||
* 2.16 - Exposes host_status for servers/detail and servers/{server_id}
|
||||
"""
|
||||
|
||||
# The minimum and maximum versions of the API supported
|
||||
@ -65,7 +66,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
# Note(cyeoh): This only applies for the v2.1 API once microversions
|
||||
# support is fully merged. It does not affect the V2 API.
|
||||
_MIN_API_VERSION = "2.1"
|
||||
_MAX_API_VERSION = "2.15"
|
||||
_MAX_API_VERSION = "2.16"
|
||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||
|
||||
|
||||
|
@ -17,16 +17,18 @@
|
||||
from nova.api.openstack import api_version_request
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import wsgi
|
||||
|
||||
from nova import compute
|
||||
|
||||
ALIAS = "os-extended-server-attributes"
|
||||
authorize = extensions.os_compute_soft_authorizer(ALIAS)
|
||||
soft_authorize = extensions.os_compute_soft_authorizer('servers')
|
||||
|
||||
|
||||
class ExtendedServerAttributesController(wsgi.Controller):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ExtendedServerAttributesController, self).__init__(*args,
|
||||
**kwargs)
|
||||
self.compute_api = compute.API(skip_policy_check=True)
|
||||
|
||||
def _extend_server(self, context, server, instance, req):
|
||||
key = "OS-EXT-SRV-ATTR:hypervisor_hostname"
|
||||
@ -44,26 +46,54 @@ class ExtendedServerAttributesController(wsgi.Controller):
|
||||
key = "OS-EXT-SRV-ATTR:%s" % attr
|
||||
server[key] = instance[attr]
|
||||
|
||||
def _server_host_status(self, context, server, instance, req):
|
||||
host_status = self.compute_api.get_instance_host_status(instance)
|
||||
server['host_status'] = host_status
|
||||
|
||||
@wsgi.extends
|
||||
def show(self, req, resp_obj, id):
|
||||
context = req.environ['nova.context']
|
||||
authorize_extend = False
|
||||
authorize_host_status = False
|
||||
if authorize(context):
|
||||
authorize_extend = True
|
||||
if (api_version_request.is_supported(req, min_version='2.16') and
|
||||
soft_authorize(context, action='show:host_status')):
|
||||
authorize_host_status = True
|
||||
if authorize_extend or authorize_host_status:
|
||||
server = resp_obj.obj['server']
|
||||
db_instance = req.get_db_instance(server['id'])
|
||||
# server['id'] is guaranteed to be in the cache due to
|
||||
# the core API adding it in its 'show' method.
|
||||
self._extend_server(context, server, db_instance, req)
|
||||
if authorize_extend:
|
||||
self._extend_server(context, server, db_instance, req)
|
||||
if authorize_host_status:
|
||||
self._server_host_status(context, server, db_instance, req)
|
||||
|
||||
@wsgi.extends
|
||||
def detail(self, req, resp_obj):
|
||||
context = req.environ['nova.context']
|
||||
authorize_extend = False
|
||||
authorize_host_status = False
|
||||
if authorize(context):
|
||||
authorize_extend = True
|
||||
if (api_version_request.is_supported(req, min_version='2.16') and
|
||||
soft_authorize(context, action='show:host_status')):
|
||||
authorize_host_status = True
|
||||
if authorize_extend or authorize_host_status:
|
||||
servers = list(resp_obj.obj['servers'])
|
||||
instances = req.get_db_instances()
|
||||
# Instances is guaranteed to be in the cache due to
|
||||
# the core API adding it in its 'detail' method.
|
||||
if authorize_host_status:
|
||||
host_statuses = self.compute_api.get_instances_host_statuses(
|
||||
instances.values())
|
||||
for server in servers:
|
||||
db_instance = req.get_db_instance(server['id'])
|
||||
# server['id'] is guaranteed to be in the cache due to
|
||||
# the core API adding it in its 'detail' method.
|
||||
self._extend_server(context, server, db_instance, req)
|
||||
if authorize_extend:
|
||||
instance = instances[server['id']]
|
||||
self._extend_server(context, server, instance, req)
|
||||
if authorize_host_status:
|
||||
server['host_status'] = host_statuses[server['id']]
|
||||
|
||||
|
||||
class ExtendedServerAttributes(extensions.V21APIExtensionBase):
|
||||
|
@ -150,3 +150,10 @@ user documentation.
|
||||
|
||||
From this version of the API users can choose 'soft-affinity' and
|
||||
'soft-anti-affinity' rules too for server-groups.
|
||||
|
||||
2.16
|
||||
----
|
||||
|
||||
Exposes new host_status attribute for servers/detail and servers/{server_id}.
|
||||
Ability to get nova-compute status when querying servers. By default, this is
|
||||
only exposed to cloud administrators.
|
||||
|
@ -67,6 +67,7 @@ from nova import notifications
|
||||
from nova import objects
|
||||
from nova.objects import base as obj_base
|
||||
from nova.objects import block_device as block_device_obj
|
||||
from nova.objects import fields as fields_obj
|
||||
from nova.objects import keypair as keypair_obj
|
||||
from nova.objects import quotas as quotas_obj
|
||||
from nova.objects import security_group as security_group_obj
|
||||
@ -3304,6 +3305,40 @@ class API(base.Base):
|
||||
self.compute_rpcapi.external_instance_event(
|
||||
context, instances_by_host[host], events_by_host[host])
|
||||
|
||||
def get_instance_host_status(self, instance):
|
||||
if instance.host:
|
||||
try:
|
||||
service = [service for service in instance.services if
|
||||
service.binary == 'nova-compute'][0]
|
||||
if service.forced_down:
|
||||
host_status = fields_obj.HostStatus.DOWN
|
||||
elif service.disabled:
|
||||
host_status = fields_obj.HostStatus.MAINTENANCE
|
||||
else:
|
||||
alive = self.servicegroup_api.service_is_up(service)
|
||||
host_status = ((alive and fields_obj.HostStatus.UP) or
|
||||
fields_obj.HostStatus.UNKNOWN)
|
||||
except IndexError:
|
||||
host_status = fields_obj.HostStatus.NONE
|
||||
else:
|
||||
host_status = fields_obj.HostStatus.NONE
|
||||
return host_status
|
||||
|
||||
def get_instances_host_statuses(self, instance_list):
|
||||
host_status_dict = dict()
|
||||
host_statuses = dict()
|
||||
for instance in instance_list:
|
||||
if instance.host:
|
||||
if instance.host not in host_status_dict:
|
||||
host_status = self.get_instance_host_status(instance)
|
||||
host_status_dict[instance.host] = host_status
|
||||
else:
|
||||
host_status = host_status_dict[instance.host]
|
||||
else:
|
||||
host_status = fields_obj.HostStatus.NONE
|
||||
host_statuses[instance.uuid] = host_status
|
||||
return host_statuses
|
||||
|
||||
|
||||
class HostAPI(base.Base):
|
||||
"""Sub-set of the Compute Manager API for managing host operations."""
|
||||
|
@ -426,8 +426,9 @@ class HostStatus(Enum):
|
||||
DOWN = "DOWN" # The nova-compute is forced_down.
|
||||
MAINTENANCE = "MAINTENANCE" # The nova-compute is disabled.
|
||||
UNKNOWN = "UNKNOWN" # The nova-compute has not reported.
|
||||
NONE = "" # No host or nova-compute.
|
||||
|
||||
ALL = (UP, DOWN, MAINTENANCE, UNKNOWN)
|
||||
ALL = (UP, DOWN, MAINTENANCE, UNKNOWN, NONE)
|
||||
|
||||
def __init__(self):
|
||||
super(HostStatus, self).__init__(
|
||||
|
@ -0,0 +1,69 @@
|
||||
{
|
||||
"server": {
|
||||
"OS-EXT-SRV-ATTR:host": "%(compute_host)s",
|
||||
"OS-EXT-SRV-ATTR:hypervisor_hostname": "%(hypervisor_hostname)s",
|
||||
"OS-EXT-SRV-ATTR:instance_name": "%(instance_name)s",
|
||||
"OS-EXT-SRV-ATTR:hostname": "new-server-test",
|
||||
"OS-EXT-SRV-ATTR:launch_index": "0",
|
||||
"OS-EXT-SRV-ATTR:reservation_id": "%(reservation_id)s",
|
||||
"OS-EXT-SRV-ATTR:root_device_name": "/dev/sda",
|
||||
"OS-EXT-SRV-ATTR:kernel_id": null,
|
||||
"OS-EXT-SRV-ATTR:ramdisk_id": null,
|
||||
"OS-EXT-SRV-ATTR:user_data": null,
|
||||
"locked": false,
|
||||
"accessIPv4": "%(access_ip_v4)s",
|
||||
"accessIPv6": "%(access_ip_v6)s",
|
||||
"updated": "%(isotime)s",
|
||||
"created": "%(isotime)s",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"addr": "%(ip)s",
|
||||
"version": 4,
|
||||
"OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff",
|
||||
"OS-EXT-IPS:type": "fixed"
|
||||
}
|
||||
]
|
||||
},
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(compute_endpoint)s/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "%(hostid)s",
|
||||
"id": "%(uuid)s",
|
||||
"image": {
|
||||
"id": "%(uuid)s",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(compute_endpoint)s/images/%(uuid)s",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"href": "%(versioned_compute_endpoint)s/servers/%(uuid)s",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "%(compute_endpoint)s/servers/%(uuid)s",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"name": "new-server-test",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"host_status": "UP",
|
||||
"tenant_id": "openstack",
|
||||
"user_id": "fake",
|
||||
"key_name": null
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
{
|
||||
"servers": [
|
||||
{
|
||||
"OS-EXT-SRV-ATTR:host": "%(compute_host)s",
|
||||
"OS-EXT-SRV-ATTR:hypervisor_hostname": "%(hypervisor_hostname)s",
|
||||
"OS-EXT-SRV-ATTR:instance_name": "%(instance_name)s",
|
||||
"OS-EXT-SRV-ATTR:hostname": "new-server-test",
|
||||
"OS-EXT-SRV-ATTR:launch_index": "0",
|
||||
"OS-EXT-SRV-ATTR:reservation_id": "%(reservation_id)s",
|
||||
"OS-EXT-SRV-ATTR:root_device_name": "/dev/sda",
|
||||
"OS-EXT-SRV-ATTR:kernel_id": null,
|
||||
"OS-EXT-SRV-ATTR:ramdisk_id": null,
|
||||
"OS-EXT-SRV-ATTR:user_data": null,
|
||||
"locked": false,
|
||||
"accessIPv4": "%(access_ip_v4)s",
|
||||
"accessIPv6": "%(access_ip_v6)s",
|
||||
"updated": "%(isotime)s",
|
||||
"created": "%(isotime)s",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"addr": "%(ip)s",
|
||||
"version": 4,
|
||||
"OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff",
|
||||
"OS-EXT-IPS:type": "fixed"
|
||||
}
|
||||
]
|
||||
},
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(compute_endpoint)s/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "%(hostid)s",
|
||||
"id": "%(uuid)s",
|
||||
"image": {
|
||||
"id": "%(uuid)s",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(compute_endpoint)s/images/%(uuid)s",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"href": "%(versioned_compute_endpoint)s/servers/%(id)s",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "%(compute_endpoint)s/servers/%(id)s",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"name": "new-server-test",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"host_status": "UP",
|
||||
"tenant_id": "openstack",
|
||||
"user_id": "fake",
|
||||
"key_name": null
|
||||
}
|
||||
]
|
||||
}
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.15",
|
||||
"version": "2.16",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.15",
|
||||
"version": "2.16",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -65,3 +65,34 @@ class ExtendedServerAttributesJsonTest(test_servers.ServersSampleBase):
|
||||
subs['access_ip_v4'] = '1.2.3.4'
|
||||
subs['access_ip_v6'] = '80fe::'
|
||||
self._verify_response('servers-detail-resp', subs, response, 200)
|
||||
|
||||
|
||||
class ExtendedServerAttributesJsonTestV216(ExtendedServerAttributesJsonTest):
|
||||
microversion = '2.16'
|
||||
scenarios = [('v2_16', {'api_major_version': 'v2.1'})]
|
||||
|
||||
def test_show(self):
|
||||
uuid = self._post_server()
|
||||
|
||||
response = self._do_get('servers/%s' % uuid)
|
||||
subs = {}
|
||||
subs['hostid'] = '[a-f0-9]+'
|
||||
subs['id'] = uuid
|
||||
subs['instance_name'] = 'instance-\d{8}'
|
||||
subs['hypervisor_hostname'] = r'[\w\.\-]+'
|
||||
subs['access_ip_v4'] = '1.2.3.4'
|
||||
subs['access_ip_v6'] = '80fe::'
|
||||
self._verify_response('server-get-resp', subs, response, 200)
|
||||
|
||||
def test_detail(self):
|
||||
uuid = self._post_server()
|
||||
|
||||
response = self._do_get('servers/detail')
|
||||
subs = {}
|
||||
subs['hostid'] = '[a-f0-9]+'
|
||||
subs['id'] = uuid
|
||||
subs['instance_name'] = 'instance-\d{8}'
|
||||
subs['hypervisor_hostname'] = r'[\w\.\-]+'
|
||||
subs['access_ip_v4'] = '1.2.3.4'
|
||||
subs['access_ip_v6'] = '80fe::'
|
||||
self._verify_response('servers-detail-resp', subs, response, 200)
|
||||
|
@ -34,6 +34,12 @@ UUID4 = '00000000-0000-0000-0000-000000000004'
|
||||
UUID5 = '00000000-0000-0000-0000-000000000005'
|
||||
|
||||
|
||||
def fake_services(host):
|
||||
service_list = [objects.Service(id=0, host=host, forced_down=True,
|
||||
binary='nova-compute')]
|
||||
return objects.ServiceList(objects=service_list)
|
||||
|
||||
|
||||
def fake_compute_get(*args, **kwargs):
|
||||
return fakes.stub_instance_obj(
|
||||
None, 1, uuid=UUID3, host="host-fake",
|
||||
@ -42,7 +48,8 @@ def fake_compute_get(*args, **kwargs):
|
||||
kernel_id=UUID4, ramdisk_id=UUID5,
|
||||
display_name="hostname-1",
|
||||
root_device_name="/dev/vda",
|
||||
user_data="userdata")
|
||||
user_data="userdata",
|
||||
services=fake_services("host-fake"))
|
||||
|
||||
|
||||
def fake_compute_get_all(*args, **kwargs):
|
||||
@ -53,14 +60,16 @@ def fake_compute_get_all(*args, **kwargs):
|
||||
kernel_id=UUID4, ramdisk_id=UUID5,
|
||||
display_name="hostname-1",
|
||||
root_device_name="/dev/vda",
|
||||
user_data="userdata"),
|
||||
user_data="userdata",
|
||||
services=fake_services("host-1")),
|
||||
fakes.stub_instance_obj(
|
||||
None, 2, uuid=UUID2, host="host-2", node="node-2",
|
||||
reservation_id="r-2", launch_index=1,
|
||||
kernel_id=UUID4, ramdisk_id=UUID5,
|
||||
display_name="hostname-2",
|
||||
root_device_name="/dev/vda",
|
||||
user_data="userdata"),
|
||||
user_data="userdata",
|
||||
services=fake_services("host-2")),
|
||||
]
|
||||
return objects.InstanceList(objects=inst_list)
|
||||
|
||||
@ -208,3 +217,36 @@ class ExtendedServerAttributesTestV23(ExtendedServerAttributesTestV21):
|
||||
hostname="hostname-%s" % (i + 1),
|
||||
root_device_name="/dev/vda",
|
||||
user_data="userdata")
|
||||
|
||||
|
||||
class ExtendedServerAttributesTestV216(ExtendedServerAttributesTestV21):
|
||||
wsgi_api_version = '2.16'
|
||||
|
||||
def assertServerAttributes(self, server, host, node, instance_name,
|
||||
host_status):
|
||||
super(ExtendedServerAttributesTestV216, self).assertServerAttributes(
|
||||
server, host, node, instance_name)
|
||||
self.assertEqual(server.get('host_status'), host_status)
|
||||
|
||||
def test_show(self):
|
||||
url = self.fake_url + '/servers/%s' % UUID3
|
||||
res = self._make_request(url)
|
||||
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertServerAttributes(self._get_server(res.body),
|
||||
host='host-fake',
|
||||
node='node-fake',
|
||||
instance_name=NAME_FMT % 1,
|
||||
host_status="DOWN")
|
||||
|
||||
def test_detail(self):
|
||||
url = self.fake_url + '/servers/detail'
|
||||
res = self._make_request(url)
|
||||
|
||||
self.assertEqual(res.status_int, 200)
|
||||
for i, server in enumerate(self._get_servers(res.body)):
|
||||
self.assertServerAttributes(server,
|
||||
host='host-%s' % (i + 1),
|
||||
node='node-%s' % (i + 1),
|
||||
instance_name=NAME_FMT % (i + 1),
|
||||
host_status="DOWN")
|
||||
|
@ -66,7 +66,7 @@ EXP_VERSIONS = {
|
||||
"v2.1": {
|
||||
"id": "v2.1",
|
||||
"status": "CURRENT",
|
||||
"version": "2.15",
|
||||
"version": "2.16",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z",
|
||||
"links": [
|
||||
@ -128,7 +128,7 @@ class VersionsTestV20(test.NoDBTestCase):
|
||||
{
|
||||
"id": "v2.1",
|
||||
"status": "CURRENT",
|
||||
"version": "2.15",
|
||||
"version": "2.16",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z",
|
||||
"links": [
|
||||
|
@ -440,7 +440,8 @@ def stub_instance(id=1, user_id=None, project_id=None, host=None,
|
||||
availability_zone='', locked_by=None, cleaned=False,
|
||||
memory_mb=0, vcpus=0, root_gb=0, ephemeral_gb=0,
|
||||
instance_type=None, launch_index=0, kernel_id="",
|
||||
ramdisk_id="", user_data=None, system_metadata=None):
|
||||
ramdisk_id="", user_data=None, system_metadata=None,
|
||||
services=None):
|
||||
if user_id is None:
|
||||
user_id = 'fake_user'
|
||||
if project_id is None:
|
||||
@ -548,7 +549,8 @@ def stub_instance(id=1, user_id=None, project_id=None, host=None,
|
||||
"pci_requests": None,
|
||||
"flavor": flavorinfo,
|
||||
},
|
||||
"cleaned": cleaned}
|
||||
"cleaned": cleaned,
|
||||
"services": services}
|
||||
|
||||
instance.update(info_cache)
|
||||
instance['info_cache']['instance_uuid'] = instance['uuid']
|
||||
@ -564,6 +566,10 @@ def stub_instance_obj(ctxt, *args, **kwargs):
|
||||
db_inst,
|
||||
expected_attrs=expected)
|
||||
inst.fault = None
|
||||
if db_inst["services"] is not None:
|
||||
# This ensures services there if one wanted so
|
||||
inst.services = db_inst["services"]
|
||||
|
||||
return inst
|
||||
|
||||
|
||||
|
@ -41,6 +41,7 @@ from nova import db
|
||||
from nova import exception
|
||||
from nova import objects
|
||||
from nova.objects import base as obj_base
|
||||
from nova.objects import fields as fields_obj
|
||||
from nova.objects import quotas as quotas_obj
|
||||
from nova import policy
|
||||
from nova import quota
|
||||
@ -70,6 +71,7 @@ class _ComputeAPIUnitTestMixIn(object):
|
||||
super(_ComputeAPIUnitTestMixIn, self).setUp()
|
||||
self.user_id = 'fake'
|
||||
self.project_id = 'fake'
|
||||
self.compute_api = compute_api.API()
|
||||
self.context = context.RequestContext(self.user_id,
|
||||
self.project_id)
|
||||
|
||||
@ -155,6 +157,13 @@ class _ComputeAPIUnitTestMixIn(object):
|
||||
instance.obj_reset_changes()
|
||||
return instance
|
||||
|
||||
def _obj_to_list_obj(self, list_obj, obj):
|
||||
list_obj.objects = []
|
||||
list_obj.objects.append(obj)
|
||||
list_obj._context = self.context
|
||||
list_obj.obj_reset_changes()
|
||||
return list_obj
|
||||
|
||||
def test_create_quota_exceeded_messages(self):
|
||||
image_href = "image_href"
|
||||
image_id = 0
|
||||
@ -3019,6 +3028,61 @@ class _ComputeAPIUnitTestMixIn(object):
|
||||
instance, 1)
|
||||
self.assertEqual('Server-%s' % instance.uuid, instance.hostname)
|
||||
|
||||
def test_host_statuses(self):
|
||||
# NOTE(tojuvone) Some test cases break utcnow() by calling
|
||||
# timeutils.set_time_override() with some own time. Have to issue a
|
||||
# bug to fix those cases to reset time back like line below so next
|
||||
# test cases will work.
|
||||
timeutils.clear_time_override()
|
||||
instances = [
|
||||
objects.Instance(uuid='uuid1', host='host1', services=
|
||||
self._obj_to_list_obj(objects.ServiceList(
|
||||
self.context), objects.Service(id=0, host='host1',
|
||||
disabled=True, forced_down=True,
|
||||
binary='nova-compute'))),
|
||||
objects.Instance(uuid='uuid2', host='host2', services=
|
||||
self._obj_to_list_obj(objects.ServiceList(
|
||||
self.context), objects.Service(id=0, host='host2',
|
||||
disabled=True, forced_down=False,
|
||||
binary='nova-compute'))),
|
||||
objects.Instance(uuid='uuid3', host='host3', services=
|
||||
self._obj_to_list_obj(objects.ServiceList(
|
||||
self.context), objects.Service(id=0, host='host3',
|
||||
disabled=False, last_seen_up=timeutils.utcnow()
|
||||
- datetime.timedelta(minutes=5),
|
||||
forced_down=False, binary='nova-compute'))),
|
||||
objects.Instance(uuid='uuid4', host='host4', services=
|
||||
self._obj_to_list_obj(objects.ServiceList(
|
||||
self.context), objects.Service(id=0, host='host4',
|
||||
disabled=False, last_seen_up=timeutils.utcnow(),
|
||||
forced_down=False, binary='nova-compute'))),
|
||||
objects.Instance(uuid='uuid5', host='host5', services=
|
||||
objects.ServiceList()),
|
||||
objects.Instance(uuid='uuid6', host=None, services=
|
||||
self._obj_to_list_obj(objects.ServiceList(
|
||||
self.context), objects.Service(id=0, host='host6',
|
||||
disabled=True, forced_down=False,
|
||||
binary='nova-compute'))),
|
||||
objects.Instance(uuid='uuid7', host='host2', services=
|
||||
self._obj_to_list_obj(objects.ServiceList(
|
||||
self.context), objects.Service(id=0, host='host2',
|
||||
disabled=True, forced_down=False,
|
||||
binary='nova-compute')))
|
||||
]
|
||||
|
||||
host_statuses = self.compute_api.get_instances_host_statuses(
|
||||
instances)
|
||||
expect_statuses = {'uuid1': fields_obj.HostStatus.DOWN,
|
||||
'uuid2': fields_obj.HostStatus.MAINTENANCE,
|
||||
'uuid3': fields_obj.HostStatus.UNKNOWN,
|
||||
'uuid4': fields_obj.HostStatus.UP,
|
||||
'uuid5': fields_obj.HostStatus.NONE,
|
||||
'uuid6': fields_obj.HostStatus.NONE,
|
||||
'uuid7': fields_obj.HostStatus.MAINTENANCE}
|
||||
for instance in instances:
|
||||
self.assertEqual(expect_statuses[instance.uuid],
|
||||
host_statuses[instance.uuid])
|
||||
|
||||
|
||||
class ComputeAPIUnitTestCase(_ComputeAPIUnitTestMixIn, test.NoDBTestCase):
|
||||
def setUp(self):
|
||||
|
@ -118,6 +118,7 @@ policy_data = """
|
||||
"os_compute_api:servers:resize": "",
|
||||
"os_compute_api:servers:revert_resize": "",
|
||||
"os_compute_api:servers:show": "",
|
||||
"os_compute_api:servers:show:host_status": "",
|
||||
"os_compute_api:servers:create_image": "",
|
||||
"os_compute_api:servers:create_image:allow_volume_backed": "",
|
||||
"os_compute_api:servers:update": "",
|
||||
|
@ -299,6 +299,7 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
|
||||
"os_compute_api:servers:create:forced_host",
|
||||
"os_compute_api:servers:detail:get_all_tenants",
|
||||
"os_compute_api:servers:index:get_all_tenants",
|
||||
"os_compute_api:servers:show:host_status",
|
||||
"network:attach_external_network",
|
||||
"os_compute_api:os-admin-actions",
|
||||
"os_compute_api:os-admin-actions:reset_network",
|
||||
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
A new host_status attribute for servers/detail and servers/{server_id}.
|
||||
In order to use this new feature, user have to contain the header of
|
||||
request microversion v2.16 in the API request. A new policy
|
||||
``os_compute_api:servers:show:host_status`` added to enable the feature.
|
||||
By default, this is only exposed to cloud administrators.
|
Loading…
Reference in New Issue
Block a user