Update VIM to use nova API version 2.53

Updating the VIM to use version 2.53 (Pike) of the nova API. The VIM
was previously using version 2.38 (Newton). The main changes are:
- Stop using os-hosts (no longer exists). Fortunately we weren't
  actually using this code.
- Use new format for os-services (server uuid now required in URL and
  body changed).
- The GET servers now returns a flavor in the response (instead of the
  flavor uuid). The VIM was previously using the uuid to look up the
  flavor in the instance_type table. Now the VIM must access the
  flavor information embedded in each instance.

Note that the server notifications from nova still include the flavor
uuid (we need to switch to the newer versioned notifications to get
the flavor). I changed our notification handling to ignore the flavor
uuid as it is no longer of any use to us.

The main reason for this change is to allow flavors to be modified or
deleted even if in use by a running instance. This brings us in line
with upstream behaviour and will allow us to stop modifying this
behaviour in nova.

Change-Id: I65fab6843174d59782aa7366f2e0cc646cebafb7
Signed-off-by: Al Bailey <Al.Bailey@windriver.com>
This commit is contained in:
Bart Wensley 2018-06-11 11:18:20 -04:00 committed by Al Bailey
parent 37f3cabe55
commit 0b06f1dbd4
31 changed files with 533 additions and 476 deletions

View File

@ -1 +1 @@
TIS_PATCH_VER=67
TIS_PATCH_VER=69

View File

@ -602,13 +602,13 @@ logs:
- name: Instance NFVI State Change
type: nfv_vim_instance_nfvi_state_change
file: _vim_nfvi_events.py
regex: "Instance state-change, nfvi_instance={'instance_type_uuid': u'(.*)',
regex: "Instance state-change, nfvi_instance={'instance_type': ({.*}),
'name': u'(.*)', 'tenant_id': '(.*)', 'avail_status': \\[(.*)], 'nfvi_data':
{'vm_state': u'(.*)', 'task_state': u?'(.*)', 'power_state': '(.*)'},
'live_migration_support': (.*), 'oper_state': '(.*)', 'host_name': u'(.*)',
'admin_state': '(.*)', 'action': '(.*)', 'image_uuid': (.*), 'uuid': u'(.*)'}."
fields:
- instance_type_uuid
- instance_type
- instance_name
- tenant_uuid
- instance_avail_status

View File

@ -448,64 +448,6 @@ class NFVIComputeAPI(nfvi.api.v1.NFVIComputeAPI):
DLOG.verbose("Auditing action requests, timer_id=%s." % timer_id)
self._ageout_action_requests()
def get_service_hosts(self, future, callback):
"""
Get a list of service hosts
"""
response = dict()
response['completed'] = False
response['reason'] = ''
try:
future.set_timeouts(config.CONF.get('nfvi-timeouts', None))
if self._token is None or self._token.is_expired():
future.work(openstack.get_token, self._directory)
future.result = (yield)
if not future.result.is_complete():
return
self._token = future.result.data
future.work(nova.get_hosts, self._token)
future.result = (yield)
if not future.result.is_complete():
return
host_data_list = future.result.data
host_objs = list()
for host_data in host_data_list['hosts']:
host_obj = nfvi_objs.ServiceHost(host_data['host_name'],
host_data['service'],
host_data['zone'])
host_objs.append(host_obj)
response['result-data'] = host_objs
response['completed'] = True
except exceptions.OpenStackRestAPIException as e:
if httplib.UNAUTHORIZED == e.http_status_code:
response['error-code'] = nfvi.NFVI_ERROR_CODE.TOKEN_EXPIRED
if self._token is not None:
self._token.set_expired()
else:
DLOG.exception("Caught exception while trying to get service "
"host list, error=%s." % e)
response['reason'] = e.http_response_reason
except Exception as e:
DLOG.exception("Caught exception while trying to get service "
"host list, error=%s." % e)
finally:
callback.send(response)
callback.close()
def get_host_aggregates(self, future, callback):
"""
Get a list of host aggregates
@ -1283,7 +1225,7 @@ class NFVIComputeAPI(nfvi.api.v1.NFVIComputeAPI):
instance_obj = nfvi_objs.Instance(
instance_data['id'], instance_data['name'],
str(tenant_uuid), admin_state, oper_state, avail_status, action,
instance_data['OS-EXT-SRV-ATTR:host'], instance_type_uuid,
instance_data['OS-EXT-SRV-ATTR:host'], instance_data['flavor'],
image_uuid, live_migration_support, attached_volumes, nfvi_data)
response['result-data'] = instance_obj
@ -2655,7 +2597,7 @@ class NFVIComputeAPI(nfvi.api.v1.NFVIComputeAPI):
tenant_uuid = uuid.UUID(instance_data['tenant_id'])
instance_type_uuid = instance_data['flavor']['id']
instance_type = instance_data['flavor']
image_data = instance_data.get('image', None)
if image_data:
@ -2686,7 +2628,7 @@ class NFVIComputeAPI(nfvi.api.v1.NFVIComputeAPI):
instance_obj = nfvi_objs.Instance(
instance_data['id'], instance_data['name'],
str(tenant_uuid), admin_state, oper_state, avail_status, action,
instance_data['OS-EXT-SRV-ATTR:host'], instance_type_uuid,
instance_data['OS-EXT-SRV-ATTR:host'], instance_type,
image_uuid, live_migration_support, attached_volumes,
nfvi_data, recovery_priority, live_migration_timeout)
@ -2751,7 +2693,6 @@ class NFVIComputeAPI(nfvi.api.v1.NFVIComputeAPI):
task_state = message.get('task_state', None)
power_state = message.get('power_state', None)
host_name = message.get('host_name', None)
instance_type_uuid = message.get('flavor_id', None)
image_uuid = message.get('image_id', None)
recovery_priority = message.get('recovery_priority', None)
live_migration_timeout = message.get('live_migration_timeout', None)
@ -2789,7 +2730,7 @@ class NFVIComputeAPI(nfvi.api.v1.NFVIComputeAPI):
admin_state, oper_state,
avail_status, action,
host_name,
instance_type_uuid,
None,
image_uuid, None, None,
nfvi_data,
recovery_priority,

View File

@ -141,3 +141,40 @@ class OpenStackRestAPIException(exceptions.PickleableException):
Returns the reason for the exception
"""
return self._reason
class NotFound(exceptions.PickleableException):
"""
Not Found Exception
"""
def __init__(self, message):
"""
Create an OpenStack exception
"""
super(NotFound, self).__init__(message)
self._message = message
def __str__(self):
"""
Return a string representing the exception
"""
return "[NotFound Exception: message=%s]" % self._message
def __repr__(self):
"""
Provide a representation of the exception
"""
return str(self)
def __reduce__(self):
"""
Return a tuple so that we can properly pickle the exception
"""
return NotFound, (self.message,)
@property
def message(self):
"""
Returns the message for the exception
"""
return self._message

View File

@ -5,19 +5,19 @@
#
import six
import json
import httplib
from nfv_common import debug
from nfv_common.helpers import Constants, Constant, Singleton
from exceptions import OpenStackRestAPIException
from exceptions import NotFound
from objects import OPENSTACK_SERVICE
from rest_api import rest_api_request, rest_api_request_with_context
DLOG = debug.debug_get_logger('nfv_plugins.nfvi_plugins.openstack.nova')
# Using maximum nova API version for newton release.
NOVA_API_VERSION = '2.38'
# Using maximum nova API version for pike release.
NOVA_API_VERSION = '2.53'
NOVA_API_VERSION_NEWTON = '2.38'
@six.add_metaclass(Singleton)
@ -199,24 +199,6 @@ def vm_power_state_str(power_state):
return "unknown"
def get_hosts(token):
"""
Asks OpenStack Nova for a list of hosts
"""
url = token.get_service_url(OPENSTACK_SERVICE.NOVA, strip_version=True)
if url is None:
raise ValueError("OpenStack Nova URL is invalid")
api_cmd = url + "/v2.1/%s/os-hosts" % token.get_tenant_id()
api_cmd_headers = dict()
api_cmd_headers['wrs-header'] = 'true'
api_cmd_headers['X-OpenStack-Nova-API-Version'] = NOVA_API_VERSION
response = rest_api_request(token, "GET", api_cmd, api_cmd_headers)
return response
def get_host_aggregates(token):
"""
Asks OpenStack Nova for a list of host aggregates
@ -1331,7 +1313,6 @@ def rpc_message_server_state_change_filter(message):
power_state = payload.get('power_state', None)
host_name = payload.get('host', None)
image_meta = payload.get('image_meta', None)
flavor_id = payload.get('instance_flavor_id', None)
image_id = image_meta.get('base_image_ref', None)
if not image_id:
@ -1360,7 +1341,6 @@ def rpc_message_server_state_change_filter(message):
state_change['task_state'] = task_state
state_change['power_state'] = power_state
state_change['host_name'] = host_name
state_change['flavor_id'] = flavor_id
state_change['image_id'] = image_id
state_change['recovery_priority'] = recovery_priority
state_change['live_migration_timeout'] = live_migration_timeout
@ -1413,7 +1393,8 @@ def create_host_services(token, host_name):
api_cmd_headers = dict()
api_cmd_headers['wrs-header'] = 'true'
api_cmd_headers['Content-Type'] = "application/json"
api_cmd_headers['X-OpenStack-Nova-API-Version'] = NOVA_API_VERSION
# The create is a WRS extension, which is not supported in Pike
api_cmd_headers['X-OpenStack-Nova-API-Version'] = NOVA_API_VERSION_NEWTON
api_cmd_payload = dict()
api_cmd_payload['binary'] = 'nova-compute'
@ -1424,6 +1405,31 @@ def create_host_services(token, host_name):
return response
def get_host_service_id(token, host_name, service_name):
"""
Asks OpenStack Nova for the service id of a service on a host
"""
url = token.get_service_url(OPENSTACK_SERVICE.NOVA, strip_version=True)
if url is None:
raise ValueError("OpenStack Nova URL is invalid")
api_cmd = url + "/v2.1/%s/os-services?host=%s&binary=%s" % \
(token.get_tenant_id(), host_name, service_name)
api_cmd_headers = dict()
api_cmd_headers['wrs-header'] = 'true'
api_cmd_headers['Content-Type'] = "application/json"
api_cmd_headers['X-OpenStack-Nova-API-Version'] = NOVA_API_VERSION
response = rest_api_request(token, "GET", api_cmd, api_cmd_headers)
services = response.result_data.get('services', list())
if services:
return services[0].get('id')
else:
raise NotFound("Service %s not found for host %s" % (service_name,
host_name))
def delete_host_services(token, host_name):
"""
Asks OpenStack Nova to delete services on a host
@ -1432,42 +1438,26 @@ def delete_host_services(token, host_name):
if url is None:
raise ValueError("OpenStack Nova URL is invalid")
compute_service_id = None
response = dict()
# Check to see if nova knows about the host or not. Nova returns
# internal-error when the host is not known on a delete.
try:
api_cmd = url + "/v2.1/%s/os-services?host=%s" % (token.get_tenant_id(),
host_name)
compute_service_id = get_host_service_id(token, host_name,
'nova-compute')
except NotFound:
# No service to delete
return response
api_cmd_headers = dict()
api_cmd_headers['wrs-header'] = 'true'
api_cmd_headers['Content-Type'] = "application/json"
api_cmd_headers['X-OpenStack-Nova-API-Version'] = NOVA_API_VERSION
api_cmd = url + "/v2.1/%s/os-services/%s" % (token.get_tenant_id(),
compute_service_id)
response = rest_api_request(token, "GET", api_cmd)
services = response.result_data.get('services', list())
for service in services:
service_name = service.get('binary', '')
if 'nova-compute' == service_name:
compute_service_id = service.get('id', None)
break
api_cmd_headers = dict()
api_cmd_headers['wrs-header'] = 'true'
api_cmd_headers['Content-Type'] = "application/json"
api_cmd_headers['X-OpenStack-Nova-API-Version'] = NOVA_API_VERSION
except OpenStackRestAPIException as e:
if httplib.NOT_FOUND != e.http_status_code:
raise
response = dict()
if compute_service_id is not None:
api_cmd = url + "/v2.1/%s/os-services/%s" % (token.get_tenant_id(),
compute_service_id)
api_cmd_headers = dict()
api_cmd_headers['wrs-header'] = 'true'
api_cmd_headers['Content-Type'] = "application/json"
api_cmd_headers['X-OpenStack-Nova-API-Version'] = NOVA_API_VERSION
response = rest_api_request(token, "DELETE", api_cmd, api_cmd_headers)
response = rest_api_request(token, "DELETE", api_cmd, api_cmd_headers)
return response
@ -1479,7 +1469,11 @@ def enable_host_services(token, host_name):
if url is None:
raise ValueError("OpenStack Nova URL is invalid")
api_cmd = url + "/v2.1/%s/os-services/enable" % token.get_tenant_id()
# Get the service ID for the nova-compute service.
compute_service_id = get_host_service_id(token, host_name, 'nova-compute')
api_cmd = url + "/v2.1/%s/os-services/%s" % (token.get_tenant_id(),
compute_service_id)
api_cmd_headers = dict()
api_cmd_headers['wrs-header'] = 'true'
@ -1487,8 +1481,7 @@ def enable_host_services(token, host_name):
api_cmd_headers['X-OpenStack-Nova-API-Version'] = NOVA_API_VERSION
api_cmd_payload = dict()
api_cmd_payload['binary'] = 'nova-compute'
api_cmd_payload['host'] = host_name
api_cmd_payload['status'] = 'enabled'
response = rest_api_request(token, "PUT", api_cmd, api_cmd_headers,
json.dumps(api_cmd_payload))
@ -1503,7 +1496,11 @@ def disable_host_services(token, host_name):
if url is None:
raise ValueError("OpenStack Nova URL is invalid")
api_cmd = url + "/v2.1/%s/os-services/disable" % token.get_tenant_id()
# Get the service ID for the nova-compute service.
compute_service_id = get_host_service_id(token, host_name, 'nova-compute')
api_cmd = url + "/v2.1/%s/os-services/%s" % (token.get_tenant_id(),
compute_service_id)
api_cmd_headers = dict()
api_cmd_headers['wrs-header'] = 'true'
@ -1511,8 +1508,8 @@ def disable_host_services(token, host_name):
api_cmd_headers['X-OpenStack-Nova-API-Version'] = NOVA_API_VERSION
api_cmd_payload = dict()
api_cmd_payload['binary'] = 'nova-compute'
api_cmd_payload['host'] = host_name
api_cmd_payload['status'] = 'disabled'
api_cmd_payload['disabled_reason'] = 'disabled by VIM'
response = rest_api_request(token, "PUT", api_cmd, api_cmd_headers,
json.dumps(api_cmd_payload))
@ -1562,7 +1559,11 @@ def notify_host_enabled(token, host_name):
if url is None:
raise ValueError("OpenStack Nova URL is invalid")
api_cmd = url + "/v2.1/%s/os-services/force-down" % token.get_tenant_id()
# Get the service ID for the nova-compute service.
compute_service_id = get_host_service_id(token, host_name, 'nova-compute')
api_cmd = url + "/v2.1/%s/os-services/%s" % (token.get_tenant_id(),
compute_service_id)
api_cmd_headers = dict()
api_cmd_headers['wrs-header'] = 'true'
@ -1570,8 +1571,6 @@ def notify_host_enabled(token, host_name):
api_cmd_headers['X-OpenStack-Nova-API-Version'] = NOVA_API_VERSION
api_cmd_payload = dict()
api_cmd_payload['binary'] = 'nova-compute'
api_cmd_payload['host'] = host_name
api_cmd_payload['forced_down'] = False
response = rest_api_request(token, "PUT", api_cmd, api_cmd_headers,
@ -1587,7 +1586,11 @@ def notify_host_disabled(token, host_name):
if url is None:
raise ValueError("OpenStack Nova URL is invalid")
api_cmd = url + "/v2.1/%s/os-services/force-down" % token.get_tenant_id()
# Get the service ID for the nova-compute service.
compute_service_id = get_host_service_id(token, host_name, 'nova-compute')
api_cmd = url + "/v2.1/%s/os-services/%s" % (token.get_tenant_id(),
compute_service_id)
api_cmd_headers = dict()
api_cmd_headers['wrs-header'] = 'true'
@ -1595,8 +1598,6 @@ def notify_host_disabled(token, host_name):
api_cmd_headers['X-OpenStack-Nova-API-Version'] = NOVA_API_VERSION
api_cmd_payload = dict()
api_cmd_payload['binary'] = 'nova-compute'
api_cmd_payload['host'] = host_name
api_cmd_payload['forced_down'] = True
response = rest_api_request(token, "PUT", api_cmd, api_cmd_headers,

View File

@ -252,9 +252,6 @@ def nova_unit_tests(token, test_config):
response = nova.enable_host_services(token, test_config['host_name'])
print "Host-Services enable response: %s" % response
hosts = nova.get_hosts(token)
print "List of hosts: %s" % hosts
hypervisors = nova.get_hypervisors(token)
print "List of hypervisors: %s" % hypervisors

View File

@ -1,7 +1,10 @@
# Get a token from keystone
TENANT_ID=`openstack token issue | grep "| project_id |" | cut -f3 -d'|' | tr -d '[[:space:]]'`
TOKEN_ID=`openstack token issue | grep "| id |" | cut -f3 -d'|' | tr -d '[[:space:]]'`
# Specify management IP
MGMT_IP=192.168.204.2
1. Create a VM
--------------
# Note:
@ -15,10 +18,12 @@ cat > compute_request.txt
"compute_data": {"flavour_id": "101",
"virtual_memory": {"virtual_mem_size": 512},
"virtual_storage": {"type_of_storage": "volume", "size_of_storage": 1},
"virtual_cpu": {"num_virtual_cpu": 1}}}
"virtual_cpu": {"num_virtual_cpu": 1}},
"meta_data": "{\"network_uuid\": \"81edbebc-d89e-40f9-bc02-7fdc9dd324f9\"}"
}
curl -i -X POST -H 'Content-Type: application/json' -H "X-Auth-Token: ${TOKEN_ID}" -H 'Accept: application/json' \
http://localhost:4545/api/virtualised-resources/computes -d @compute_request.txt
http://${MGMT_IP}:4545/api/virtualised-resources/computes -d @compute_request.txt
2. Delete a VM
@ -26,19 +31,19 @@ http://localhost:4545/api/virtualised-resources/computes -d @compute_request.txt
VM_ID=8bdb775c-9e29-4412-a49f-0895b5faa76a
curl -i -X DELETE -H 'Content-Type: application/json' -H "X-Auth-Token: ${TOKEN_ID}" -H 'Accept: application/json' \
http://localhost:4545/api/virtualised-resources/computes/${VM_ID}
http://${MGMT_IP}:4545/api/virtualised-resources/computes/${VM_ID}
3. Get a VM
-----------
VM_ID=8bdb775c-9e29-4412-a49f-0895b5faa76a
curl -i -X GET -H "X-Auth-Token: ${TOKEN_ID}" -H 'Accept: application/json' \
http://localhost:4545/api/virtualised-resources/computes/${VM_ID}
http://${MGMT_IP}:4545/api/virtualised-resources/computes/${VM_ID}
4. Get all VMs
--------------
curl -i -X GET -H "X-Auth-Token: ${TOKEN_ID}" -H 'Accept: application/json' \
http://localhost:4545/api/virtualised-resources/computes
http://${MGMT_IP}:4545/api/virtualised-resources/computes
5. Pause a VM
@ -46,68 +51,67 @@ http://localhost:4545/api/virtualised-resources/computes
VM_ID=8bdb775c-9e29-4412-a49f-0895b5faa76a
curl -i -X POST -H 'Content-Type: application/json' -H "X-Auth-Token: ${TOKEN_ID}" -H 'Accept: application/json' \
http://localhost:4545/api/virtualised-resources/computes/${VM_ID}/operate -d "{\"compute_operation\": \"pause\"}"
http://${MGMT_IP}:4545/api/virtualised-resources/computes/${VM_ID}/operate -d "{\"compute_operation\": \"pause\"}"
6. Unpause a VM
---------------
VM_ID=8bdb775c-9e29-4412-a49f-0895b5faa76a
curl -i -X POST -H 'Content-Type: application/json' -H "X-Auth-Token: ${TOKEN_ID}" -H 'Accept: application/json' \
http://localhost:4545/api/virtualised-resources/computes/${VM_ID}/operate -d "{\"compute_operation\": \"unpause\"}"
http://${MGMT_IP}:4545/api/virtualised-resources/computes/${VM_ID}/operate -d "{\"compute_operation\": \"unpause\"}"
7. Suspend a VM
---------------
VM_ID=8bdb775c-9e29-4412-a49f-0895b5faa76a
curl -i -X POST -H 'Content-Type: application/json' -H "X-Auth-Token: ${TOKEN_ID}" -H 'Accept: application/json' \
http://localhost:4545/api/virtualised-resources/computes/${VM_ID}/operate -d "{\"compute_operation\": \"suspend\"}"
http://${MGMT_IP}:4545/api/virtualised-resources/computes/${VM_ID}/operate -d "{\"compute_operation\": \"suspend\"}"
8. Resume a VM
--------------
VM_ID=8bdb775c-9e29-4412-a49f-0895b5faa76a
curl -i -X POST -H 'Content-Type: application/json' -H "X-Auth-Token: ${TOKEN_ID}" -H 'Accept: application/json' \
http://localhost:4545/api/virtualised-resources/computes/${VM_ID}/operate -d "{\"compute_operation\": \"resume\"}"
http://${MGMT_IP}:4545/api/virtualised-resources/computes/${VM_ID}/operate -d "{\"compute_operation\": \"resume\"}"
9. Stop a VM
------------
VM_ID=8bdb775c-9e29-4412-a49f-0895b5faa76a
curl -i -X POST -H 'Content-Type: application/json' -H "X-Auth-Token: ${TOKEN_ID}" -H 'Accept: application/json' \
http://localhost:4545/api/virtualised-resources/computes/${VM_ID}/operate -d "{\"compute_operation\": \"stop\"}"
http://${MGMT_IP}:4545/api/virtualised-resources/computes/${VM_ID}/operate -d "{\"compute_operation\": \"stop\"}"
10. Start a VM
--------------
VM_ID=8bdb775c-9e29-4412-a49f-0895b5faa76a
curl -i -X POST -H 'Content-Type: application/json' -H "X-Auth-Token: ${TOKEN_ID}" -H 'Accept: application/json' \
http://localhost:4545/api/virtualised-resources/computes/${VM_ID}/operate -d "{\"compute_operation\": \"start\"}"
http://${MGMT_IP}:4545/api/virtualised-resources/computes/${VM_ID}/operate -d "{\"compute_operation\": \"start\"}"
11. Reboot a VM
---------------
VM_ID=8bdb775c-9e29-4412-a49f-0895b5faa76a
curl -i -X POST -H 'Content-Type: application/json' -H "X-Auth-Token: ${TOKEN_ID}" -H 'Accept: application/json' \
http://localhost:4545/api/virtualised-resources/computes/${VM_ID}/operate -d "{\"compute_operation\": \"reboot\"}"
http://${MGMT_IP}:4545/api/virtualised-resources/computes/${VM_ID}/operate -d "{\"compute_operation\": \"reboot\"}"
12. Live-Migrate a VM
---------------------
VM_ID=8bdb775c-9e29-4412-a49f-0895b5faa76a
curl -i -X POST -H 'Content-Type: application/json' -H "X-Auth-Token: ${TOKEN_ID}" -H 'Accept: application/json' \
http://localhost:4545/api/virtualised-resources/computes/${VM_ID}/migrate -d "{\"migrate_type\": \"live\"}"
http://${MGMT_IP}:4545/api/virtualised-resources/computes/${VM_ID}/migrate -d "{\"migrate_type\": \"live\"}"
13. Cold-Migrate a VM
---------------------
VM_ID=8bdb775c-9e29-4412-a49f-0895b5faa76a
curl -i -X POST -H 'Content-Type: application/json' -H "X-Auth-Token: ${TOKEN_ID}" -H 'Accept: application/json' \
http://localhost:4545/api/virtualised-resources/computes/${VM_ID}/migrate -d "{\"migrate_type\": \"cold\"}"
http://${MGMT_IP}:4545/api/virtualised-resources/computes/${VM_ID}/migrate -d "{\"migrate_type\": \"cold\"}"
14. Evacuate a VM
-----------------
VM_ID=8bdb775c-9e29-4412-a49f-0895b5faa76a
curl -i -X POST -H 'Content-Type: application/json' -H "X-Auth-Token: ${TOKEN_ID}" -H 'Accept: application/json' \
http://localhost:4545/api/virtualised-resources/computes/${VM_ID}/migrate -d "{\"migrate_type\": \"evacuate\"}"
http://${MGMT_IP}:4545/api/virtualised-resources/computes/${VM_ID}/migrate -d "{\"migrate_type\": \"evacuate\"}"

View File

@ -841,7 +841,7 @@ class TestInstanceResize(TestInstance):
flavor_id = None
for flavor_name in self._flavor_names:
flavor_id = self._get_flavor_id(flavor_name)
if flavor_id != self._instance_data['flavor']['id']:
if flavor_name != self._instance_data['flavor']['original_name']:
self._flavor_id = flavor_id
break
@ -919,7 +919,7 @@ class TestInstanceResizeConfirm(TestInstance):
flavor_id = None
for flavor_name in self._flavor_names:
flavor_id = self._get_flavor_id(flavor_name)
if flavor_id != self._instance_data['flavor']['id']:
if flavor_name != self._instance_data['flavor']['original_name']:
break
if flavor_id is None:
@ -1015,7 +1015,7 @@ class TestInstanceResizeRevert(TestInstance):
flavor_id = None
for flavor_name in self._flavor_names:
flavor_id = self._get_flavor_id(flavor_name)
if flavor_id != self._instance_data['flavor']['id']:
if flavor_name != self._instance_data['flavor']['original_name']:
break
if flavor_id is None:

File diff suppressed because one or more lines are too long

View File

@ -10,16 +10,16 @@ from nfv_vim import database
from nfv_vim import tables
def test_nfv_vim_database_upgrade_from_17_06():
def test_nfv_vim_database_upgrade_from_18_03():
"""
Test VIM database upgrades from 17.06 GA
Test VIM database upgrades from 18.03 GA
"""
root_dir = os.environ['VIRTUAL_ENV']
devnull = open(os.devnull, 'w')
try:
vim_cmd = ("nfv-vim-manage db-load-data -d %s "
"-f %s/nfv_vim_db_17.06_GA" % (root_dir, root_dir))
"-f %s/nfv_vim_db_18.03_GA" % (root_dir, root_dir))
subprocess.check_call([vim_cmd], shell=True, stderr=devnull)

View File

@ -24,6 +24,8 @@ from nfv_vim.tables._instance_group_table import InstanceGroupTable
from nfv_vim.nfvi.objects import v1 as nfvi_objects
import utils
# Constants
# Globals
@ -73,7 +75,8 @@ def create_instance(instance_name, instance_type_name, image_name, host_name,
avail_status=list(),
action=nfvi.objects.v1.INSTANCE_ACTION.NONE,
host_name=host_name,
instance_type_uuid=instance_type.uuid,
instance_type=utils.instance_type_to_flavor_dict(
instance_type),
image_uuid=image.uuid,
live_migration_timeout=live_migration_timeout)

View File

@ -13,6 +13,8 @@ from nfv_vim import objects
from nfv_vim.tables._table import Table
from nfv_vim.directors._instance_director import InstanceDirector
import utils
# Constants
_audit_interval = 330
_audit_cooldown = 30
@ -50,7 +52,9 @@ def create_instance(instance_type_name, instance_name, recovery_priority=None):
oper_state=nfvi.objects.v1.INSTANCE_OPER_STATE.ENABLED,
avail_status=list(),
action=nfvi.objects.v1.INSTANCE_ACTION.NONE,
host_name='compute-0', instance_type_uuid=instance_type.uuid,
host_name='compute-0',
instance_type=utils.instance_type_to_flavor_dict(
instance_type),
image_uuid=image_uuid,
recovery_priority=recovery_priority)
@ -176,10 +180,10 @@ def test_instance_director_recovery_list_order(
instance_1._elapsed_time_in_state = _recovery_cooldown
instance_1._nfvi_instance.avail_status.append(
nfvi.objects.v1.INSTANCE_AVAIL_STATUS.FAILED)
instance_1._vcpus = 1
instance_1._memory_mb = 32
instance_1._disk_gb = 2
instance_1._swap_gb = 0
instance_1._nfvi_instance['instance_type']['vcpus'] = 1
instance_1._nfvi_instance['instance_type']['ram'] = 32
instance_1._nfvi_instance['instance_type']['disk'] = 2
instance_1._nfvi_instance['instance_type']['swap'] = 0
_instance_table[instance_1.uuid] = instance_1
# Validate the Instance Director recovery_list order
@ -195,10 +199,10 @@ def test_instance_director_recovery_list_order(
instance_2._elapsed_time_in_state = _recovery_cooldown
instance_2._nfvi_instance.avail_status.append(
nfvi.objects.v1.INSTANCE_AVAIL_STATUS.FAILED)
instance_2._vcpus = 2
instance_2._memory_mb = 32
instance_2._disk_gb = 1
instance_2._swap_gb = 0
instance_2._nfvi_instance['instance_type']['vcpus'] = 2
instance_2._nfvi_instance['instance_type']['ram'] = 32
instance_2._nfvi_instance['instance_type']['disk'] = 1
instance_2._nfvi_instance['instance_type']['swap'] = 0
_instance_table[instance_2.uuid] = instance_2
# -- with two instances in the failed state
@ -215,10 +219,10 @@ def test_instance_director_recovery_list_order(
instance_3._elapsed_time_in_state = _recovery_cooldown
instance_3._nfvi_instance.avail_status.append(
nfvi.objects.v1.INSTANCE_AVAIL_STATUS.FAILED)
instance_3._vcpus = 1
instance_3._memory_mb = 32
instance_3._disk_gb = 0
instance_3._swap_gb = 0
instance_3._nfvi_instance['instance_type']['vcpus'] = 1
instance_3._nfvi_instance['instance_type']['ram'] = 32
instance_3._nfvi_instance['instance_type']['disk'] = 0
instance_3._nfvi_instance['instance_type']['swap'] = 0
_instance_table[instance_3.uuid] = instance_3
# -- with three instances in the failed state
@ -236,10 +240,10 @@ def test_instance_director_recovery_list_order(
instance_4._elapsed_time_in_state = _recovery_cooldown
instance_4._nfvi_instance.avail_status.append(
nfvi.objects.v1.INSTANCE_AVAIL_STATUS.FAILED)
instance_4._vcpus = 1
instance_4._memory_mb = 32
instance_4._disk_gb = 0
instance_4._swap_gb = 0
instance_4._nfvi_instance['instance_type']['vcpus'] = 1
instance_4._nfvi_instance['instance_type']['ram'] = 32
instance_4._nfvi_instance['instance_type']['disk'] = 0
instance_4._nfvi_instance['instance_type']['swap'] = 0
_instance_table[instance_4.uuid] = instance_4
# -- with four instances in the failed state
@ -258,10 +262,10 @@ def test_instance_director_recovery_list_order(
instance_5._elapsed_time_in_state = _recovery_cooldown
instance_5._nfvi_instance.avail_status.append(
nfvi.objects.v1.INSTANCE_AVAIL_STATUS.FAILED)
instance_5._vcpus = 2
instance_5._memory_mb = 32
instance_5._disk_gb = 0
instance_5._swap_gb = 0
instance_5._nfvi_instance['instance_type']['vcpus'] = 2
instance_5._nfvi_instance['instance_type']['ram'] = 32
instance_5._nfvi_instance['instance_type']['disk'] = 0
instance_5._nfvi_instance['instance_type']['swap'] = 0
_instance_table[instance_5.uuid] = instance_5
# -- with five instances in the failed state
@ -301,7 +305,6 @@ def test_instance_director_recover_instance(
dor_is_complete_mock.return_value = True
instance_1 = create_instance('small', 'instance_1')
instance_1._auto_recovery = mock.Mock(return_value=True)
instance_1.fail = mock.Mock()
instance_1.do_action = mock.Mock()
instance_1._nfvi_instance.avail_status.append(

View File

@ -23,6 +23,8 @@ from nfv_vim.tables._instance_table import InstanceTable
from nfv_vim.tables._instance_group_table import InstanceGroupTable
from nfv_vim.strategy._strategy import SwPatchStrategy, strategy_rebuild_from_dict
import utils
# Constants
# Globals
@ -67,7 +69,9 @@ def create_instance(instance_type_name, instance_name, host_name,
oper_state=nfvi.objects.v1.INSTANCE_OPER_STATE.ENABLED,
avail_status=list(),
action=nfvi.objects.v1.INSTANCE_ACTION.NONE,
host_name=host_name, instance_type_uuid=instance_type.uuid,
host_name=host_name,
instance_type=utils.instance_type_to_flavor_dict(
instance_type),
image_uuid=image_uuid)
instance = objects.Instance(nfvi_instance)

View File

@ -26,6 +26,8 @@ from nfv_vim.strategy._strategy import SwUpgradeStrategy, strategy_rebuild_from_
from nfv_vim.nfvi.objects.v1 import UPGRADE_STATE
import utils
# Constants
# Globals
@ -70,7 +72,8 @@ def create_instance(instance_type_name, instance_name, host_name,
oper_state=nfvi.objects.v1.INSTANCE_OPER_STATE.ENABLED,
avail_status=list(),
action=nfvi.objects.v1.INSTANCE_ACTION.NONE,
host_name=host_name, instance_type_uuid=instance_type.uuid,
host_name=host_name,
instance_type=utils.instance_type_to_flavor_dict(instance_type),
image_uuid=image_uuid)
instance = objects.Instance(nfvi_instance)

View File

@ -0,0 +1,32 @@
#
# Copyright (c) 2018 Wind River Systems, Inc.
#
# The right to copy, distribute, modify, or otherwise make use
# of this software may be licensed only pursuant to the terms
# of an applicable Wind River license agreement.
#
from nfv_vim import nfvi
def instance_type_to_flavor_dict(instance_type):
flavor = dict()
flavor['vcpus'] = instance_type.vcpus
flavor['ram'] = instance_type.mem_mb
flavor['disk'] = instance_type.disk_gb
flavor['ephemeral'] = instance_type.ephemeral_gb
flavor['swap'] = instance_type.swap_gb
flavor['original_name'] = 'JustAName'
extra_specs = dict()
extra_specs[
nfvi.objects.v1.INSTANCE_TYPE_EXTENSION.LIVE_MIGRATION_TIMEOUT] = \
instance_type.live_migration_timeout
extra_specs[
nfvi.objects.v1.INSTANCE_TYPE_EXTENSION.LIVE_MIGRATION_MAX_DOWNTIME] = \
instance_type.live_migration_max_downtime
extra_specs[
nfvi.objects.v1.INSTANCE_TYPE_EXTENSION.STORAGE_TYPE] = \
instance_type.storage_type
flavor['extra_specs'] = extra_specs
return flavor

View File

@ -72,7 +72,7 @@ deps = {[nfv]deps}
coverage
setenv = PYTHONDONTWRITEBYTECODE=True
#commands = nosetests --pdb --exe -w tests/
commands = cp -v ./test_data/nfv_vim_db_17.06_GA {envdir}/
commands = cp -v ./test_data/nfv_vim_db_18.03_GA {envdir}/
{envbindir}/nosetests --exe -w tests/ '{posargs}'
# Use the following for code coverage
# {envbindir}/nosetests --exe --cover-erase --cover-branches \

View File

@ -315,6 +315,7 @@ class ComputeQueryAttributesResourceType(wsme_types.Base):
acceleration_capabilities = unicode
virtual_memory = ComputeQueryVirtualMemoryType
virtual_cpu = ComputeQueryVirtualCpuType
flavour_original_name = unicode
class ComputeQueryResourceType(wsme_types.Base):
@ -376,9 +377,11 @@ class ComputesAPI(pecan.rest.RestController):
virtual_cpu.num_virtual_cpu = response.vcpus
compute_attributes = ComputeQueryAttributesResourceType()
compute_attributes.flavour_id = response.instance_type_uuid
compute_attributes.flavour_id = ''
compute_attributes.virtual_memory = virtual_memory
compute_attributes.virtual_cpu = virtual_cpu
compute_attributes.flavour_original_name = \
response.instance_type_original_name
query_result = ComputeQueryResourceType()
query_result.compute_id = response.uuid
@ -448,9 +451,11 @@ class ComputesAPI(pecan.rest.RestController):
virtual_cpu.num_virtual_cpu = response.vcpus
compute_attributes = ComputeQueryAttributesResourceType()
compute_attributes.flavour_id = response.instance_type_uuid
compute_attributes.flavour_id = ''
compute_attributes.virtual_memory = virtual_memory
compute_attributes.virtual_cpu = virtual_cpu
compute_attributes.flavour_original_name = \
response.instance_type_original_name
query_result = ComputeQueryResourceType()
query_result.compute_id = response.uuid
@ -523,9 +528,11 @@ class ComputesAPI(pecan.rest.RestController):
virtual_cpu.num_virtual_cpu = response.vcpus
compute_attributes = ComputeQueryAttributesResourceType()
compute_attributes.flavour_id = response.instance_type_uuid
compute_attributes.flavour_id = ''
compute_attributes.virtual_memory = virtual_memory
compute_attributes.virtual_cpu = virtual_cpu
compute_attributes.flavour_original_name = \
response.instance_type_original_name
query_result = ComputeQueryResourceType()
query_result.compute_id = response.uuid

View File

@ -247,11 +247,11 @@ def database_instance_add(instance_obj):
"""
db = database_get()
session = db.session()
query = session.query(model.Instance_v4)
query = query.filter(model.Instance_v4.uuid == instance_obj.uuid)
query = session.query(model.Instance_v5)
query = query.filter(model.Instance_v5.uuid == instance_obj.uuid)
instance = query.first()
if not instance:
instance = model.Instance_v4()
instance = model.Instance_v5()
instance.uuid = instance_obj.uuid
instance.name = instance_obj.name
instance.admin_state = instance_obj.admin_state
@ -259,7 +259,6 @@ def database_instance_add(instance_obj):
instance.avail_status = json.dumps(instance_obj.avail_status)
instance.action = instance_obj.action
instance.host_name = instance_obj.host_name
instance.instance_type_uuid = instance_obj.instance_type_uuid
instance.image_uuid = instance_obj.image_uuid
instance.live_migration_support = instance_obj.supports_live_migration()
instance.elapsed_time_in_state = instance_obj.elapsed_time_in_state
@ -281,7 +280,6 @@ def database_instance_add(instance_obj):
instance.avail_status = json.dumps(instance_obj.avail_status)
instance.action = instance_obj.action
instance.host_name = instance_obj.host_name
instance.instance_type_uuid = instance_obj.instance_type_uuid
instance.image_uuid = instance_obj.image_uuid
instance.live_migration_support = instance_obj.supports_live_migration()
instance.elapsed_time_in_state = instance_obj.elapsed_time_in_state
@ -304,8 +302,8 @@ def database_instance_delete(instance_uuid):
"""
db = database_get()
session = db.session()
query = session.query(model.Instance_v4)
query.filter(model.Instance_v4.uuid == instance_uuid).delete()
query = session.query(model.Instance_v5)
query.filter(model.Instance_v5.uuid == instance_uuid).delete()
session.commit()
@ -315,7 +313,7 @@ def database_instance_get_list():
"""
db = database_get()
session = db.session()
query = session.query(model.Instance_v4)
query = session.query(model.Instance_v5)
instance_objs = list()
for instance in query.all():
last_action_data_data = json.loads(instance.last_action_data)
@ -403,7 +401,7 @@ def database_instance_get_list():
nfvi_instance_data['avail_status'],
nfvi_instance_data['action'],
nfvi_instance_data['host_name'],
nfvi_instance_data['instance_type_uuid'],
nfvi_instance_data['instance_type'],
nfvi_instance_data['image_uuid'],
nfvi_instance_data['live_migration_support'],
attached_volumes,

View File

@ -12,33 +12,77 @@ from nfv_common import debug
DLOG = debug.debug_get_logger('nfv_vim.database')
def _migrate_instances_v3_to_v4(session, instances_v3, instances_v4):
def _migrate_instances_v4_to_v5(session, instances_v4, instances_v5):
"""
Migrate instances_v3 table to instances_v4 table
Migrate instances_v4 table to instances_v5 table
"""
if 0 == len(instances_v4):
for instance_v3 in instances_v3:
instance_v4 = model.Instance_v4()
instance_v4.data = instance_v3.data
nfvi_instance_data = json.loads(instance_v3.nfvi_instance_data)
# Use previous recovery_priority if it exists, otherwise use None
nfvi_instance_data['recovery_priority'] = \
nfvi_instance_data.get('recovery_priority', None)
# Use previous timeout if it exists, otherwise use None
nfvi_instance_data['live_migration_timeout'] = \
nfvi_instance_data.get('live_migration_timeout', None)
instance_v4.nfvi_instance_data = json.dumps(nfvi_instance_data)
session.add(instance_v4)
if 0 == len(instances_v5):
instance_type_query = session.query(model.InstanceType)
instance_types = instance_type_query.all()
for instance_v4 in instances_v4:
instance_v5 = model.Instance_v5()
instance_type_uuid = instance_v4.instance_type_uuid
del instance_v4.data['instance_type_uuid']
instance_v5.data = instance_v4.data
nfvi_instance_data = json.loads(instance_v4.nfvi_instance_data)
# We can build the flavor details embedded in the instance from
# the flavor referenced from the original instance.
for instance_type in instance_types:
if instance_type.uuid == instance_type_uuid:
break
else:
DLOG.error("Missing instance type: %s" % instance_type_uuid)
continue
flavor = dict()
flavor['vcpus'] = instance_type.vcpus
flavor['ram'] = instance_type.mem_mb
flavor['disk'] = instance_type.disk_gb
flavor['ephemeral'] = instance_type.ephemeral_gb
flavor['swap'] = instance_type.swap_gb
flavor['original_name'] = instance_type.name
# Re-create the flavor extra_specs, undoing all the mangling that
# the VIM did when converting the flavor to an instance_type.
extra_specs = dict()
guest_services = instance_type.guest_services
if 'heartbeat' in guest_services:
if guest_services['heartbeat'] == 'configured':
extra_specs['sw:wrs:guest:heartbeat'] = 'true'
else:
extra_specs['sw:wrs:guest:heartbeat'] = 'false'
if instance_type.auto_recovery is not None:
if instance_type.auto_recovery:
extra_specs['sw:wrs:auto_recovery'] = 'true'
else:
extra_specs['sw:wrs:auto_recovery'] = 'false'
if instance_type.live_migration_timeout is not None:
extra_specs['hw:wrs:live_migration_timeout'] = \
instance_type.live_migration_timeout
if instance_type.live_migration_max_downtime is not None:
extra_specs['hw:wrs:live_migration_max_downtime'] = \
instance_type.live_migration_max_downtime
if instance_type.storage_type is not None:
extra_specs['aggregate_instance_extra_specs:storage'] = \
instance_type.storage_type
if extra_specs:
flavor['extra_specs'] = extra_specs
nfvi_instance_data['instance_type'] = flavor
instance_v5.nfvi_instance_data = json.dumps(nfvi_instance_data)
session.add(instance_v5)
def migrate_tables(session, table_names):
"""
Migrate database tables
"""
if 'instances_v3' in table_names and 'instances_v4' in table_names:
instances_v3_query = session.query(model.Instance_v3)
instances_v3 = instances_v3_query.all()
if 'instances_v4' in table_names and 'instances_v5' in table_names:
instances_v4_query = session.query(model.Instance_v4)
instances_v4 = instances_v4_query.all()
_migrate_instances_v3_to_v4(session, instances_v3, instances_v4)
instances_v3_query.delete()
instances_v5_query = session.query(model.Instance_v5)
instances_v5 = instances_v5_query.all()
_migrate_instances_v4_to_v5(session, instances_v4, instances_v5)
instances_v4_query.delete()

View File

@ -7,24 +7,6 @@ import sys
import types
def _upgrade_instance_types_v1(table_row_data):
"""
Upgrade instance_types_v1 table to the current release
"""
# Once all the patches for 15.12 have been released this code is not needed.
# The VIM on startup will re-sync with Nova.
return None
def _upgrade_volumes(table_row_data):
"""
Upgrade volumes table to the current release
"""
# Once all the patches for 15.12 have been released this code is not needed.
# The VIM on startup will re-sync with Cinder.
return None
def upgrade_table_row_data(table_name, table_row_data):
"""
Upgrade a database table row data

View File

@ -21,5 +21,5 @@ from _service_host import ServiceHost
from _hypervisor import Hypervisor
from _instance_type import InstanceType
from _instance_group import InstanceGroup
from _instance import Instance_v3, Instance_v4
from _instance import Instance_v4, Instance_v5
from _sw_update import SoftwareUpdate

View File

@ -3,44 +3,14 @@
#
# SPDX-License-Identifier: Apache-2.0
#
from sqlalchemy import Column, String, Boolean
from sqlalchemy import Column, String, Boolean, Text
from _base import Base, AsDictMixin
class Instance_v3(AsDictMixin, Base):
"""
Instance Database Table
"""
__tablename__ = 'instances_v3'
uuid = Column(String(64), nullable=False, primary_key=True)
name = Column(String(64), nullable=False)
admin_state = Column(String(64), nullable=False)
oper_state = Column(String(64), nullable=False)
avail_status = Column(String(64), nullable=False)
action = Column(String(64), nullable=False)
host_name = Column(String(64), nullable=True)
instance_type_uuid = Column(String(64), nullable=False)
image_uuid = Column(String(64), nullable=True)
live_migration_support = Column(Boolean, nullable=False)
elapsed_time_in_state = Column(String(64), nullable=False)
elapsed_time_on_host = Column(String(64), nullable=False)
action_data = Column(String(2048), nullable=True)
last_action_data = Column(String(2048), nullable=True)
guest_services = Column(String(2048), nullable=True)
nfvi_instance_data = Column(String(2048), nullable=False)
recoverable = Column(Boolean, nullable=False)
unlock_to_recover = Column(Boolean, nullable=False)
def __repr__(self):
return "<Instance(%r, %r)>" % (self.uuid, self.name)
class Instance_v4(AsDictMixin, Base):
"""
Instance Database Table
Note: Upversioned due to changes in the nfvi_instance_data.
"""
__tablename__ = 'instances_v4'
@ -65,3 +35,31 @@ class Instance_v4(AsDictMixin, Base):
def __repr__(self):
return "<Instance(%r, %r)>" % (self.uuid, self.name)
class Instance_v5(AsDictMixin, Base):
"""
Instance Database Table
"""
__tablename__ = 'instances_v5'
uuid = Column(String(64), nullable=False, primary_key=True)
name = Column(String(64), nullable=False)
admin_state = Column(String(64), nullable=False)
oper_state = Column(String(64), nullable=False)
avail_status = Column(String(64), nullable=False)
action = Column(String(64), nullable=False)
host_name = Column(String(64), nullable=True)
image_uuid = Column(String(64), nullable=True)
live_migration_support = Column(Boolean, nullable=False)
elapsed_time_in_state = Column(String(64), nullable=False)
elapsed_time_on_host = Column(String(64), nullable=False)
action_data = Column(String(2048), nullable=True)
last_action_data = Column(String(2048), nullable=True)
guest_services = Column(String(2048), nullable=True)
nfvi_instance_data = Column(Text(), nullable=False)
recoverable = Column(Boolean, nullable=False)
unlock_to_recover = Column(Boolean, nullable=False)
def __repr__(self):
return "<Instance(%r, %r)>" % (self.uuid, self.name)

View File

@ -2250,7 +2250,17 @@ def instance_manage_events(instance, enabling=False):
event_id = event_log.EVENT_ID.INSTANCE_COLD_MIGRATE_REVERTED
elif last_event(event_log.EVENT_ID.INSTANCE_RESIZING):
if instance.from_instance_type_uuid == instance.instance_type_uuid:
# Note: This isn't going to work, because the unversioned
# notifications we get from nova do not include the flavor details.
# When we switch to use the versioned notifications, they will
# include the flavor. However, I have verified that the original
# reason for this clause (CGTS-4099) no longer needs this code -
# nova will explicitly fail a resize if the disk size in the new
# flavor is smaller than the old flavor (instead of silently
# failing). I am leaving this code here in case there are some
# other silent failures we want to catch in the future.
if instance.from_instance_type_original_name == \
instance.instance_type_original_name:
event_id = event_log.EVENT_ID.INSTANCE_RESIZE_FAILED
elif last_event(event_log.EVENT_ID.INSTANCE_RESIZE_CONFIRM_BEGIN):

View File

@ -34,7 +34,8 @@ def _create_instance_callback(success, instance_name, instance_uuid):
response.avail_status = instance.avail_status
response.action = instance.action
response.host_name = instance.host_name
response.instance_type_uuid = instance.instance_type_uuid
response.instance_type_original_name \
= instance.instance_type_original_name
response.image_uuid = instance.image_uuid
response.vcpus = instance.vcpus
response.memory_mb = instance.memory_mb
@ -301,7 +302,8 @@ def vim_instance_api_get_instance(connection, msg):
response.avail_status = instance.avail_status
response.action = instance.action
response.host_name = instance.host_name
response.instance_type_uuid = instance.instance_type_uuid
response.instance_type_original_name \
= instance.instance_type_original_name
response.image_uuid = instance.image_uuid
response.vcpus = instance.vcpus
response.memory_mb = instance.memory_mb
@ -341,7 +343,8 @@ def vim_instance_api_get_instances(connection, msg):
response.avail_status = instance.avail_status
response.action = instance.action
response.host_name = instance.host_name
response.instance_type_uuid = instance.instance_type_uuid
response.instance_type_original_name \
= instance.instance_type_original_name
response.image_uuid = instance.image_uuid
response.vcpus = instance.vcpus
response.memory_mb = instance.memory_mb

View File

@ -227,12 +227,18 @@ def _nfvi_instance_state_change_callback(nfvi_instance):
nfvi.nfvi_get_instance(nfvi_instance.uuid,
_query_nfvi_instance_callback())
else:
# Make sure we don't overwrite some information, if it isn't available
# We are handling a notification from nova, which will not have all
# the data for the nfvi_instance we store as part of our instance
# object. As part of the processing of the notification, we are going
# to save the nfvi_instance to the database, so make sure we don't
# overwrite data that did not come in the notification, by retrieving
# it from the instance object. Yes - this is ugly. No - I'm not going
# to rewrite this now.
if nfvi_instance.tenant_id is None:
nfvi_instance.tenant_id = instance.tenant_uuid
if nfvi_instance.instance_type_uuid is None:
nfvi_instance.instance_type_uuid = instance.instance_type_uuid
if nfvi_instance.instance_type is None:
nfvi_instance.instance_type = instance._nfvi_instance.instance_type
if nfvi_instance.image_uuid is None:
nfvi_instance.image_uuid = instance.image_uuid

View File

@ -78,7 +78,6 @@ from _nfvi_network_module import nfvi_update_subnet
from _nfvi_network_module import nfvi_delete_subnet
from _nfvi_network_module import nfvi_get_subnet
from _nfvi_network_module import nfvi_get_subnets
from _nfvi_compute_module import nfvi_get_service_hosts
from _nfvi_compute_module import nfvi_get_host_aggregates
from _nfvi_compute_module import nfvi_get_hypervisors
from _nfvi_compute_module import nfvi_get_hypervisor

View File

@ -12,15 +12,6 @@ DLOG = debug.debug_get_logger('nfv_vim.nfvi.nfvi_compute_module')
_compute_plugin = None
def nfvi_get_service_hosts(callback):
"""
Get a list of service hosts
"""
cmd_id = _compute_plugin.invoke_plugin('get_service_hosts',
callback=callback)
return cmd_id
def nfvi_get_host_aggregates(callback):
"""
Get a list of host aggregates

View File

@ -40,13 +40,6 @@ class NFVIComputeAPI(object):
"""
pass
@abc.abstractmethod
def get_service_hosts(self, future, callback):
"""
Get a list of service hosts from the plugin
"""
pass
@abc.abstractmethod
def get_host_aggregates(self, future, callback):
"""

View File

@ -9,6 +9,7 @@ from datetime import datetime
from _object import ObjectData
from nfv_common.helpers import Object, Constant, Constants, Singleton
from _instance_type import INSTANCE_TYPE_EXTENSION
@six.add_metaclass(Singleton)
@ -389,7 +390,7 @@ class Instance(ObjectData):
NFVI Instance Object
"""
def __init__(self, uuid, name, tenant_id, admin_state, oper_state,
avail_status, action, host_name, instance_type_uuid,
avail_status, action, host_name, instance_type,
image_uuid=None, live_migration_support=None,
attached_volumes=None, nfvi_data=None,
recovery_priority=None, live_migration_timeout=None):
@ -402,7 +403,7 @@ class Instance(ObjectData):
admin_state=admin_state, oper_state=oper_state,
avail_status=avail_status, action=action,
host_name=host_name,
instance_type_uuid=instance_type_uuid,
instance_type=instance_type,
image_uuid=image_uuid,
live_migration_support=live_migration_support,
attached_volumes=attached_volumes,
@ -410,3 +411,125 @@ class Instance(ObjectData):
live_migration_timeout=live_migration_timeout))
self.nfvi_data = nfvi_data
@property
def instance_type_vcpus(self):
"""
Returns the vcpus from the flavor
"""
return self.get('instance_type').get('vcpus')
@property
def instance_type_mem_mb(self):
"""
Returns the ram from the flavor
"""
return self.get('instance_type').get('ram')
@property
def instance_type_disk_gb(self):
"""
Returns the disk from the flavor
"""
return self.get('instance_type').get('disk')
@property
def instance_type_ephemeral_gb(self):
"""
Returns the ephemeral from the flavor
"""
return self.get('instance_type').get('ephemeral')
@property
def instance_type_swap_gb(self):
"""
Returns the swap from the flavor
"""
return self.get('instance_type').get('swap')
@property
def instance_type_original_name(self):
"""
Returns the original name from the flavor
"""
return self.get('instance_type').get('original_name')
@property
def instance_type_guest_services(self):
"""
Returns the guest services from the flavor extra specs
"""
guest_services = dict()
flavor_data_extra = self.get('instance_type').get('extra_specs', None)
if flavor_data_extra is not None:
heartbeat = flavor_data_extra.get(
INSTANCE_TYPE_EXTENSION.GUEST_HEARTBEAT, None)
if heartbeat and 'true' == heartbeat.lower():
guest_heartbeat = INSTANCE_GUEST_SERVICE_STATE.CONFIGURED
else:
guest_heartbeat = None
if guest_heartbeat is not None:
guest_services['heartbeat'] = guest_heartbeat
return guest_services
@property
def instance_type_auto_recovery(self):
"""
Returns the auto recovery from the flavor extra specs
"""
auto_recovery = None
flavor_data_extra = self.get('instance_type').get('extra_specs', None)
if flavor_data_extra is not None:
auto_recovery = flavor_data_extra.get(
INSTANCE_TYPE_EXTENSION.INSTANCE_AUTO_RECOVERY, None)
if auto_recovery is not None:
if 'false' == auto_recovery.lower():
auto_recovery = False
elif 'true' == auto_recovery.lower():
auto_recovery = True
else:
raise AttributeError("sw:wrs:auto_recovery is %s, "
"expecting 'true' or 'false'"
% auto_recovery)
return auto_recovery
@property
def instance_type_live_migration_timeout(self):
"""
Returns the live migration timeout from the flavor extra specs
"""
live_migration_timeout = None
flavor_data_extra = self.get('instance_type').get('extra_specs', None)
if flavor_data_extra is not None:
live_migration_timeout = flavor_data_extra.get(
INSTANCE_TYPE_EXTENSION.LIVE_MIGRATION_TIMEOUT, None)
return live_migration_timeout
@property
def instance_type_live_migration_max_downtime(self):
"""
Returns the live migration max downtime from the flavor extra specs
"""
live_migration_max_downtime = None
flavor_data_extra = self.get('instance_type').get('extra_specs', None)
if flavor_data_extra is not None:
live_migration_max_downtime = flavor_data_extra.get(
INSTANCE_TYPE_EXTENSION.LIVE_MIGRATION_MAX_DOWNTIME, None)
return live_migration_max_downtime
@property
def instance_type_storage_type(self):
"""
Returns the storage type from the flavor extra specs
"""
storage_type = None
flavor_data_extra = self.get('instance_type').get('extra_specs', None)
if flavor_data_extra is not None:
storage_type = flavor_data_extra.get(
INSTANCE_TYPE_EXTENSION.STORAGE_TYPE, None)
return storage_type

View File

@ -805,16 +805,10 @@ class Instance(ObjectData):
self._events = list()
self._guest_heartbeat_alarms = list()
self._guest_heartbeat_events = list()
self._vcpus = None
self._memory_mb = None
self._disk_gb = None
self._ephemeral_gb = None
self._swap_gb = None
self._live_migrate_from_host = None
self._cold_migrate_from_host = None
self._evacuate_from_host = None
self._resize_from_instance_type = None
self._auto_recovery = None
self._resize_from_instance_type_original_name = None
self._max_live_migrate_wait_in_secs = None
self._max_live_migration_downtime_in_ms = None
self._max_cold_migrate_wait_in_secs = None
@ -957,20 +951,20 @@ class Instance(ObjectData):
return None
@property
def instance_type_uuid(self):
def instance_type_original_name(self):
"""
Returns the instance type identifier
Returns the instance type original name
"""
if self._nfvi_instance is not None:
return self._nfvi_instance.instance_type_uuid
return self._nfvi_instance.instance_type_original_name
return None
@property
def from_instance_type_uuid(self):
def from_instance_type_original_name(self):
"""
Returns the from instance type identifier
Returns the from instance type original name
"""
return self._resize_from_instance_type
return self._resize_from_instance_type_original_name
@property
def image_uuid(self):
@ -1091,100 +1085,35 @@ class Instance(ObjectData):
"""
Returns the number of vcpus needed for the instance
"""
from nfv_vim import tables
if self._vcpus is not None:
return self._vcpus
instance_type_table = tables.tables_get_instance_type_table()
instance_type = instance_type_table.get(self.instance_type_uuid, None)
if instance_type is not None:
if instance_type.have_details():
self._vcpus = instance_type.vcpus
if self._vcpus is None:
return 0
return self._vcpus
return self._nfvi_instance.instance_type_vcpus
@property
def memory_mb(self):
"""
Returns the memory needed for this instance
"""
from nfv_vim import tables
if self._memory_mb is not None:
return self._memory_mb
instance_type_table = tables.tables_get_instance_type_table()
instance_type = instance_type_table.get(self.instance_type_uuid, None)
if instance_type is not None:
if instance_type.have_details():
self._memory_mb = instance_type.mem_mb
if self._memory_mb is None:
return 0
return self._memory_mb
return self._nfvi_instance.instance_type_mem_mb
@property
def disk_gb(self):
"""
Returns the disk size needed for this instance
"""
from nfv_vim import tables
if self._disk_gb is not None:
return self._disk_gb
instance_type_table = tables.tables_get_instance_type_table()
instance_type = instance_type_table.get(self.instance_type_uuid, None)
if instance_type is not None:
if instance_type.have_details():
self._disk_gb = instance_type.disk_gb
if self._disk_gb is None:
return 0
return self._disk_gb
return self._nfvi_instance.instance_type_disk_gb
@property
def ephemeral_gb(self):
"""
Returns the swap size needed for this instance
Returns the ephemeral size needed for this instance
"""
from nfv_vim import tables
if self._ephemeral_gb is not None:
return self._ephemeral_gb
instance_type_table = tables.tables_get_instance_type_table()
instance_type = instance_type_table.get(self.instance_type_uuid, None)
if instance_type is not None:
if instance_type.have_details():
self._ephemeral_gb = instance_type.ephemeral_gb
if self._ephemeral_gb is None:
return 0
return self._ephemeral_gb
return self._nfvi_instance.instance_type_ephemeral_gb
@property
def swap_gb(self):
"""
Returns the swap size needed for this instance
"""
from nfv_vim import tables
if self._swap_gb is not None:
return self._swap_gb
instance_type_table = tables.tables_get_instance_type_table()
instance_type = instance_type_table.get(self.instance_type_uuid, None)
if instance_type is not None:
if instance_type.have_details():
self._swap_gb = instance_type.swap_gb
if self._swap_gb is None:
return 0
return self._swap_gb
return self._nfvi_instance.instance_type_swap_gb
@property
def fail_reason(self):
@ -1226,20 +1155,13 @@ class Instance(ObjectData):
"""
Returns the guest services for this instance
"""
from nfv_vim import tables
instance_type_table = tables.tables_get_instance_type_table()
if instance_type_table is not None:
instance_type = instance_type_table.get(self.instance_type_uuid,
None)
if instance_type is not None:
if instance_type.have_details():
if instance_type.guest_services:
for service in instance_type.guest_services.keys():
self._guest_services.provision(service)
else:
if self._guest_services.are_provisioned():
self._guest_services.delete()
if self._nfvi_instance.instance_type_guest_services:
for service in \
self._nfvi_instance.instance_type_guest_services.keys():
self._guest_services.provision(service)
else:
if self._guest_services.are_provisioned():
self._guest_services.delete()
return self._guest_services
@ -1300,34 +1222,21 @@ class Instance(ObjectData):
"""
from nfv_vim import tables
if self._auto_recovery is not None:
DLOG.debug("Auto-Recovery is %s for %s." % (self._auto_recovery,
self.name))
return self._auto_recovery
instance_type_table = tables.tables_get_instance_type_table()
if instance_type_table is not None:
instance_type = instance_type_table.get(self.instance_type_uuid,
None)
if instance_type is not None:
if instance_type.have_details() \
and instance_type.auto_recovery is not None:
self._auto_recovery = instance_type.auto_recovery
auto_recovery = self._nfvi_instance.instance_type_auto_recovery
# instance type attributes overwrite image ones
if self._auto_recovery is None:
if auto_recovery is None:
image_table = tables.tables_get_image_table()
image = image_table.get(self.image_uuid, None)
if image is not None and image.auto_recovery is not None:
self._auto_recovery = image.auto_recovery
auto_recovery = image.auto_recovery
# turn on instance auto recovery by default
if self._auto_recovery is None:
self._auto_recovery = True
if auto_recovery is None:
auto_recovery = True
DLOG.debug("Auto-Recovery set to %s for %s."
% (self._auto_recovery, self.name))
return self._auto_recovery
DLOG.debug("Auto-Recovery is %s for %s." % (auto_recovery, self.name))
return auto_recovery
@property
def max_live_migrate_wait_in_secs(self):
@ -1344,16 +1253,10 @@ class Instance(ObjectData):
timeout_from_image = int(image.live_migration_timeout)
# check the flavor for the live migration timeout
timeout_from_flavor = None
instance_type_table = tables.tables_get_instance_type_table()
if instance_type_table is not None:
instance_type = instance_type_table.get(self.instance_type_uuid,
None)
if instance_type is not None:
if instance_type.have_details() \
and instance_type.live_migration_timeout is not None:
timeout_from_flavor \
= int(instance_type.live_migration_timeout)
timeout_from_flavor = \
self._nfvi_instance.instance_type_live_migration_timeout
if timeout_from_flavor is not None:
timeout_from_flavor = int(timeout_from_flavor)
# check the instance for the live migration timeout
timeout_from_instance = None
@ -1454,15 +1357,10 @@ class Instance(ObjectData):
= image.live_migration_max_downtime
# instance type attributes overwrite image ones
instance_type_table = tables.tables_get_instance_type_table()
if instance_type_table is not None:
instance_type = instance_type_table.get(self.instance_type_uuid,
None)
if instance_type is not None:
if instance_type.have_details() \
and instance_type.live_migration_max_downtime is not None:
self._max_live_migration_downtime_in_ms \
= instance_type.live_migration_max_downtime
if self._nfvi_instance.instance_type_live_migration_max_downtime is \
not None:
self._max_live_migration_downtime_in_ms = \
self._nfvi_instance.instance_type_live_migration_max_downtime
# convert value to integer
if self._max_live_migration_downtime_in_ms is not None:
@ -1534,8 +1432,6 @@ class Instance(ObjectData):
"""
Returns true if the instance can be cold-migrated
"""
from nfv_vim import tables
if not system_initiated:
# Always allow user initiated cold migration
return True
@ -1544,13 +1440,7 @@ class Instance(ObjectData):
# Always allow cold migration when booted from a volume
return True
storage_type = None
instance_type_table = tables.tables_get_instance_type_table()
if instance_type_table is not None:
instance_type = instance_type_table.get(self.instance_type_uuid,
None)
if instance_type is not None and instance_type.have_details():
storage_type = instance_type.storage_type
storage_type = self._nfvi_instance.instance_type_storage_type
if STORAGE_TYPE.REMOTE_BACKED == storage_type:
# Always allow cold migration with remote storage
@ -1580,8 +1470,6 @@ class Instance(ObjectData):
"""
Returns true if the instance can be evacuated
"""
from nfv_vim import tables
if not system_initiated:
# Always allow user initiated evacuate
return True
@ -1590,13 +1478,7 @@ class Instance(ObjectData):
# Always allow evacuate when booted from a volume
return True
storage_type = None
instance_type_table = tables.tables_get_instance_type_table()
if instance_type_table is not None:
instance_type = instance_type_table.get(self.instance_type_uuid,
None)
if instance_type is not None and instance_type.have_details():
storage_type = instance_type.storage_type
storage_type = self._nfvi_instance.instance_type_storage_type
if STORAGE_TYPE.REMOTE_BACKED == storage_type:
# Always allow evacuate with remote storage
@ -1626,27 +1508,20 @@ class Instance(ObjectData):
"""
Returns true if this instance supports live-migration
"""
from nfv_vim import tables
if self._live_migration_support is not None:
if not self._live_migration_support:
return False
instance_type_table = tables.tables_get_instance_type_table()
if instance_type_table is not None:
instance_type = instance_type_table.get(self.instance_type_uuid,
None)
if instance_type is not None and instance_type.have_details():
if self.image_uuid is None:
if (instance_type.swap_gb or
instance_type.ephemeral_gb):
return (STORAGE_TYPE.REMOTE_BACKED ==
instance_type.storage_type)
return True
else:
return (instance_type.storage_type in [
STORAGE_TYPE.LOCAL_IMAGE_BACKED,
STORAGE_TYPE.REMOTE_BACKED])
if self.image_uuid is None:
if (self._nfvi_instance.instance_type_swap_gb or
self._nfvi_instance.instance_type_ephemeral_gb):
return (STORAGE_TYPE.REMOTE_BACKED ==
self._nfvi_instance.instance_type_storage_type)
return True
else:
return (self._nfvi_instance.instance_type_storage_type in [
STORAGE_TYPE.LOCAL_IMAGE_BACKED,
STORAGE_TYPE.REMOTE_BACKED])
return True
@ -2649,13 +2524,14 @@ class Instance(ObjectData):
if nfvi.objects.v1.INSTANCE_ACTION.REBUILDING != self.action:
cleanup_evacuate_from_host = True
cleanup_resize_from_instance_type = False
cleanup_resize_from_instance_type_original_name = False
if nfvi.objects.v1.INSTANCE_ACTION.RESIZING == nfvi_action:
if nfvi.objects.v1.INSTANCE_ACTION.RESIZING != self.action:
self._resize_from_instance_type = self.instance_type_uuid
self._resize_from_instance_type_original_name = \
self._nfvi_instance.instance_type_original_name
else:
if nfvi.objects.v1.INSTANCE_ACTION.RESIZING != self.action:
cleanup_resize_from_instance_type = True
cleanup_resize_from_instance_type_original_name = True
if nfvi.objects.v1.INSTANCE_AVAIL_STATUS.CRASHED \
in nfvi_avail_status:
@ -2713,8 +2589,8 @@ class Instance(ObjectData):
if cleanup_evacuate_from_host:
self._evacuate_from_host = None
if cleanup_resize_from_instance_type:
self._resize_from_instance_type = None
if cleanup_resize_from_instance_type_original_name:
self._resize_from_instance_type_original_name = None
if need_recovery:
instance_director.recover_instance(self, force_fail=True)
@ -3137,7 +3013,6 @@ class Instance(ObjectData):
data['action'] = self.action
data['host_name'] = self.host_name
data['image_uuid'] = self.image_uuid
data['instance_type_uuid'] = self.instance_type_uuid
data['action_data'] = self.action_data.as_dict()
data['last_action_data'] = self.last_action_data.as_dict()
if self.guest_services.are_provisioned():

View File

@ -81,7 +81,7 @@ class APIResponseCreateInstance(RPCMessage):
action = None
host_uuid = None
host_name = None
instance_type_uuid = None
instance_type_original_name = None
image_uuid = None
vcpus = None
memory_mb = None
@ -108,7 +108,7 @@ class APIResponseCreateInstance(RPCMessage):
msg['action'] = self.action
msg['host_uuid'] = self.host_uuid
msg['host_name'] = self.host_name
msg['instance_type_uuid'] = self.instance_type_uuid
msg['instance_type_original_name'] = self.instance_type_original_name
msg['image_uuid'] = self.image_uuid
msg['vcpus'] = self.vcpus
msg['memory_mb'] = self.memory_mb
@ -130,7 +130,8 @@ class APIResponseCreateInstance(RPCMessage):
self.action = msg.get('action', None)
self.host_uuid = msg.get('host_uuid', None)
self.host_name = msg.get('host_name', None)
self.instance_type_uuid = msg.get('instance_type_uuid', None)
self.instance_type_original_name = msg.get(
'instance_type_original_name', None)
self.image_uuid = msg.get('image_uuid', None)
self.vcpus = msg.get('vcpus', None)
self.memory_mb = msg.get('memory_mb', None)
@ -678,7 +679,7 @@ class APIResponseGetInstance(RPCMessage):
action = None
host_uuid = None
host_name = None
instance_type_uuid = None
instance_type_original_name = None
image_uuid = None
vcpus = None
memory_mb = None
@ -704,7 +705,7 @@ class APIResponseGetInstance(RPCMessage):
msg['action'] = self.action
msg['host_uuid'] = self.host_uuid
msg['host_name'] = self.host_name
msg['instance_type_uuid'] = self.instance_type_uuid
msg['instance_type_original_name'] = self.instance_type_original_name
msg['image_uuid'] = self.image_uuid
msg['vcpus'] = self.vcpus
msg['memory_mb'] = self.memory_mb
@ -725,7 +726,8 @@ class APIResponseGetInstance(RPCMessage):
self.action = msg.get('action', None)
self.host_uuid = msg.get('host_uuid', None)
self.host_name = msg.get('host_name', None)
self.instance_type_uuid = msg.get('instance_type_uuid', None)
self.instance_type_original_name = msg.get(
'instance_type_original_name', None)
self.image_uuid = msg.get('image_uuid', None)
self.vcpus = msg.get('vcpus', None)
self.memory_mb = msg.get('memory_mb', None)