nfv/nfv/nfv-plugins/nfv_plugins/nfvi_plugins/nfvi_guest_api.py
Dan Voiculeasa c63057a699 py3: Fix http server request handler wfile write parameter
BaseHTTPServer.BaseHTTPRequestHandler.wfile.write requires encoded
bytes.

The purpose of this commit is to unblock sanity on centos8 branch.
The main test was sysinv periodic audit function receiving the correct
response when querying the software patching status.
If there are any inconsistencies of encoding schemes between
components they will be discovered and fixed later.

Story: 2008454
Task: 42727
Signed-off-by: Dan Voiculeasa <dan.voiculeasa@windriver.com>
Change-Id: I5f288cf85f42cb98b4211f6b9d836fafcf19e31b
(cherry picked from commit 56dfd11348)
2021-07-26 13:12:58 -04:00

1251 lines
48 KiB
Python
Executable File

#
# Copyright (c) 2015-2018 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import json
from six.moves import http_client as httplib
from nfv_common import debug
from nfv_vim import nfvi
from nfv_plugins.nfvi_plugins import config
from nfv_plugins.nfvi_plugins.openstack import exceptions
from nfv_plugins.nfvi_plugins.openstack import guest
from nfv_plugins.nfvi_plugins.openstack import openstack
from nfv_plugins.nfvi_plugins.openstack import rest_api
from nfv_plugins.nfvi_plugins.openstack.objects import OPENSTACK_SERVICE
DLOG = debug.debug_get_logger('nfv_plugins.nfvi_plugins.guest_api')
def guest_service_get_name(name):
"""
Convert the nfvi guest service naming
"""
if guest.GUEST_SERVICE_NAME.HEARTBEAT == name:
return nfvi.objects.v1.GUEST_SERVICE_NAME.HEARTBEAT
else:
return nfvi.objects.v1.GUEST_SERVICE_NAME.UNKNOWN
def guest_service_get_admin_state(state):
"""
Convert the nfvi guest service state to a guest service administrative
state
"""
if guest.GUEST_SERVICE_STATE.ENABLED == state:
return nfvi.objects.v1.GUEST_SERVICE_ADMIN_STATE.UNLOCKED
else:
return nfvi.objects.v1.GUEST_SERVICE_ADMIN_STATE.LOCKED
def guest_service_get_service_state(state):
"""
Convert the guest service administrative state to nfvi guest service state
"""
if nfvi.objects.v1.GUEST_SERVICE_ADMIN_STATE.UNLOCKED == state:
return guest.GUEST_SERVICE_STATE.ENABLED
else:
return guest.GUEST_SERVICE_STATE.DISABLED
def guest_service_get_oper_state(status):
"""
Convert the nfvi guest service status to a guest service operational
state
"""
if guest.GUEST_SERVICE_STATUS.ENABLED == status:
return nfvi.objects.v1.GUEST_SERVICE_OPER_STATE.ENABLED
else:
return nfvi.objects.v1.GUEST_SERVICE_OPER_STATE.DISABLED
def instance_get_event(action_type, pre_notification):
"""
Convert the action type to a guest instance event
"""
if nfvi.objects.v1.INSTANCE_ACTION_TYPE.PAUSE == action_type:
event = guest.GUEST_EVENT.PAUSE
elif nfvi.objects.v1.INSTANCE_ACTION_TYPE.UNPAUSE == action_type:
event = guest.GUEST_EVENT.UNPAUSE
elif nfvi.objects.v1.INSTANCE_ACTION_TYPE.SUSPEND == action_type:
event = guest.GUEST_EVENT.SUSPEND
elif nfvi.objects.v1.INSTANCE_ACTION_TYPE.RESUME == action_type:
event = guest.GUEST_EVENT.RESUME
elif nfvi.objects.v1.INSTANCE_ACTION_TYPE.REBOOT == action_type:
event = guest.GUEST_EVENT.REBOOT
elif nfvi.objects.v1.INSTANCE_ACTION_TYPE.STOP == action_type:
event = guest.GUEST_EVENT.STOP
elif nfvi.objects.v1.INSTANCE_ACTION_TYPE.LIVE_MIGRATE == action_type:
if pre_notification:
event = guest.GUEST_EVENT.LIVE_MIGRATE_BEGIN
else:
event = guest.GUEST_EVENT.LIVE_MIGRATE_END
elif nfvi.objects.v1.INSTANCE_ACTION_TYPE.COLD_MIGRATE == action_type:
if pre_notification:
event = guest.GUEST_EVENT.COLD_MIGRATE_BEGIN
else:
event = guest.GUEST_EVENT.COLD_MIGRATE_END
elif nfvi.objects.v1.INSTANCE_ACTION_TYPE.RESIZE == action_type:
if pre_notification:
event = guest.GUEST_EVENT.RESIZE_BEGIN
else:
event = guest.GUEST_EVENT.RESIZE_END
elif nfvi.objects.v1.INSTANCE_ACTION_TYPE.CONFIRM_RESIZE == action_type:
event = guest.GUEST_EVENT.RESIZE_END
elif nfvi.objects.v1.INSTANCE_ACTION_TYPE.REVERT_RESIZE == action_type:
event = guest.GUEST_EVENT.RESIZE_END
else:
event = guest.GUEST_EVENT.UNKNOWN
DLOG.error("Unsupported action-type %s" % action_type)
return event
def instance_get_action_type(event):
"""
Convert guest instance event to an action type
"""
if guest.GUEST_EVENT.PAUSE == event:
action_type = nfvi.objects.v1.INSTANCE_ACTION_TYPE.PAUSE
elif guest.GUEST_EVENT.UNPAUSE == event:
action_type = nfvi.objects.v1.INSTANCE_ACTION_TYPE.UNPAUSE
elif guest.GUEST_EVENT.SUSPEND == event:
action_type = nfvi.objects.v1.INSTANCE_ACTION_TYPE.SUSPEND
elif guest.GUEST_EVENT.RESUME == event:
action_type = nfvi.objects.v1.INSTANCE_ACTION_TYPE.RESUME
elif guest.GUEST_EVENT.REBOOT == event:
action_type = nfvi.objects.v1.INSTANCE_ACTION_TYPE.REBOOT
elif guest.GUEST_EVENT.STOP == event:
action_type = nfvi.objects.v1.INSTANCE_ACTION_TYPE.STOP
elif guest.GUEST_EVENT.LIVE_MIGRATE_BEGIN == event:
action_type = nfvi.objects.v1.INSTANCE_ACTION_TYPE.LIVE_MIGRATE
elif guest.GUEST_EVENT.LIVE_MIGRATE_END == event:
action_type = nfvi.objects.v1.INSTANCE_ACTION_TYPE.LIVE_MIGRATE
elif guest.GUEST_EVENT.COLD_MIGRATE_BEGIN == event:
action_type = nfvi.objects.v1.INSTANCE_ACTION_TYPE.COLD_MIGRATE
elif guest.GUEST_EVENT.COLD_MIGRATE_END == event:
action_type = nfvi.objects.v1.INSTANCE_ACTION_TYPE.COLD_MIGRATE
elif guest.GUEST_EVENT.RESIZE_BEGIN == event:
action_type = nfvi.objects.v1.INSTANCE_ACTION_TYPE.RESIZE
elif guest.GUEST_EVENT.RESIZE_END == event:
action_type = nfvi.objects.v1.INSTANCE_ACTION_TYPE.RESIZE
else:
action_type = nfvi.objects.v1.INSTANCE_ACTION_TYPE.UNKNOWN
DLOG.error("Unsupported event %s" % event)
return action_type
def get_services_with_guest_service_state(services):
"""
Return guest service data with guest service administrative state
converted to nfvi guest service state
"""
services_data = []
for service in services:
service_data = dict()
service_data['service'] = service['service']
service_data['state'] \
= guest_service_get_service_state(service['admin_state'])
services_data.append(service_data)
return services_data
class NFVIGuestAPI(nfvi.api.v1.NFVIGuestAPI):
"""
NFVI Guest API Class Definition
"""
_name = 'Guest-API'
_version = '1.0.0'
_provider = 'Wind River'
_signature = '22b3dbf6-e4ba-441b-8797-fb8a51210a43'
@property
def name(self):
return self._name
@property
def version(self):
return self._version
@property
def provider(self):
return self._provider
@property
def signature(self):
return self._signature
def __init__(self):
super(NFVIGuestAPI, self).__init__()
self._token = None
self._directory = None
self._openstack_directory = None
self._rest_api_server = None
self._host_services_query_callback = None
self._guest_services_query_callback = None
self._guest_services_state_notify_callbacks = list()
self._guest_services_alarm_notify_callbacks = list()
self._guest_services_action_notify_callbacks = list()
def _host_supports_nova_compute(self, personality):
return (('worker' in personality) and
(self._openstack_directory.get_service_info(
OPENSTACK_SERVICE.NOVA) is not None))
def guest_services_create(self, future, instance_uuid, host_name,
services, callback):
"""
Guest Services Create
"""
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() or \
future.result.data is None:
DLOG.error("OpenStack get-token did not complete, "
"instance_uuid=%s." % instance_uuid)
return
self._token = future.result.data
future.work(guest.guest_services_create, self._token,
instance_uuid, host_name, services)
future.result = (yield)
if not future.result.is_complete():
return
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 create "
"guest services, error=%s." % e)
except Exception as e:
DLOG.exception("Caught exception while trying to create "
"guest services, instance_uuid=%s, error=%s."
% (instance_uuid, e))
finally:
callback.send(response)
callback.close()
def guest_services_set(self, future, instance_uuid, host_name,
services, callback):
"""
Guest Services Set
"""
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() or \
future.result.data is None:
DLOG.error("OpenStack get-token did not complete, "
"instance_uuid=%s." % instance_uuid)
return
self._token = future.result.data
services_data = get_services_with_guest_service_state(services)
future.work(guest.guest_services_set, self._token,
instance_uuid, host_name, services_data)
future.result = (yield)
if not future.result.is_complete():
return
set_data = future.result.data
result_data = dict()
result_data['instance_uuid'] = set_data['uuid']
result_data['host_name'] = set_data['hostname']
service_objs = list()
for service in set_data.get('services', list()):
service_name = guest_service_get_name(service['service'])
admin_state = guest_service_get_admin_state(service['state'])
oper_state = guest_service_get_oper_state(service['status'])
restart_timeout = service.get('restart-timeout', None)
if restart_timeout is not None:
restart_timeout = int(restart_timeout)
service_obj = nfvi.objects.v1.GuestService(
service_name, admin_state, oper_state, restart_timeout)
service_objs.append(service_obj)
result_data['services'] = service_objs
response['result-data'] = result_data
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 set "
"guest services, error=%s." % e)
except Exception as e:
DLOG.exception("Caught exception while trying to set "
"guest services, instance_uuid=%s, error=%s."
% (instance_uuid, e))
finally:
callback.send(response)
callback.close()
def guest_services_delete(self, future, instance_uuid, callback):
"""
Guest Services Delete
"""
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() or \
future.result.data is None:
DLOG.error("OpenStack get-token did not complete, "
"instance_uuid=%s." % instance_uuid)
return
self._token = future.result.data
future.work(guest.guest_services_delete, self._token,
instance_uuid)
try:
future.result = (yield)
if not future.result.is_complete():
return
except exceptions.OpenStackRestAPIException as e:
if httplib.NOT_FOUND != e.http_status_code:
raise
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 delete "
"guest services, error=%s." % e)
except Exception as e:
DLOG.exception("Caught exception while trying to delete "
"guest services, instance_uuid=%s, error=%s."
% (instance_uuid, e))
finally:
callback.send(response)
callback.close()
def guest_services_query(self, future, instance_uuid, callback):
"""
Guest Services Query
"""
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() or \
future.result.data is None:
DLOG.error("OpenStack get-token did not complete, "
"instance_uuid=%s." % instance_uuid)
return
self._token = future.result.data
future.work(guest.guest_services_query, self._token, instance_uuid)
future.result = (yield)
if not future.result.is_complete():
DLOG.error("Guest-Services query failed, operation "
"did not complete, instance_uuid=%s" %
instance_uuid)
return
query_data = future.result.data
result_data = dict()
result_data['instance_uuid'] = query_data['uuid']
result_data['host_name'] = query_data['hostname']
service_objs = list()
for service in query_data.get('services', list()):
service_name = guest_service_get_name(service['service'])
admin_state = guest_service_get_admin_state(service['state'])
oper_state = guest_service_get_oper_state(service['status'])
restart_timeout = service.get('restart-timeout', None)
if restart_timeout is not None:
restart_timeout = int(restart_timeout)
service_obj = nfvi.objects.v1.GuestService(
service_name, admin_state, oper_state, restart_timeout)
service_objs.append(service_obj)
result_data['services'] = service_objs
response['result-data'] = result_data
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 query "
"guest services, error=%s." % e)
except Exception as e:
DLOG.exception("Caught exception while trying to query "
"guest services, instance_uuid=%s, error=%s."
% (instance_uuid, e))
finally:
callback.send(response)
callback.close()
def guest_services_vote(self, future, instance_uuid, host_name,
action_type, callback):
"""
Guest Services Vote
"""
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() or \
future.result.data is None:
DLOG.error("OpenStack get-token did not complete, "
"instance_uuid=%s." % instance_uuid)
return
self._token = future.result.data
future.work(guest.guest_services_vote, self._token, instance_uuid,
host_name, instance_get_event(action_type,
pre_notification=True))
future.result = (yield)
if not future.result.is_complete():
DLOG.error("Guest-Services vote failed, action-type %s "
"did not complete, instance_uuid=%s" %
(action_type, instance_uuid))
return
vote_data = future.result.data
response['uuid'] = vote_data.get('uuid', instance_uuid)
response['host-name'] = vote_data.get('hostname', host_name)
response['action-type'] = vote_data.get('action', action_type)
response['timeout'] = int(vote_data.get('timeout', '15'))
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 vote,"
"action_type=%s error=%s." % (action_type, e))
except Exception as e:
DLOG.exception("Caught exception while trying to vote "
"guest services, instance_uuid=%s, error=%s."
% (instance_uuid, e))
finally:
callback.send(response)
callback.close()
def guest_services_notify(self, future, instance_uuid, host_name,
action_type, pre_notification, callback):
"""
Guest Services Notify
"""
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() or \
future.result.data is None:
DLOG.error("OpenStack get-token did not complete, "
"instance_uuid=%s." % instance_uuid)
return
self._token = future.result.data
future.work(guest.guest_services_notify, self._token,
instance_uuid, host_name,
instance_get_event(action_type, pre_notification))
future.result = (yield)
if not future.result.is_complete():
DLOG.error("Guest-Services_notify failed, action-type %s "
"did not complete, instance_uuid=%s" %
(action_type, instance_uuid))
return
notify_data = future.result.data
response['uuid'] = notify_data.get('uuid', instance_uuid)
response['host-name'] = notify_data.get('hostname', host_name)
response['action-type'] = notify_data.get('action', action_type)
response['timeout'] = int(notify_data.get('timeout', '15'))
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 notify,"
"action_type=%s error=%s." % (action_type, e))
except Exception as e:
DLOG.exception("Caught exception while trying to notify "
"guest services, instance_uuid=%s, error=%s."
% (instance_uuid, e))
finally:
callback.send(response)
callback.close()
def create_host_services(self, future, host_uuid, host_name,
host_personality, callback):
"""
Create Host Services, notify Guest to create services for a host
"""
response = dict()
response['completed'] = False
response['reason'] = ''
try:
future.set_timeouts(config.CONF.get('nfvi-timeouts', None))
if self._host_supports_nova_compute(host_personality):
response['reason'] = 'failed to get platform token from ' \
'keystone'
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() or \
future.result.data is None:
DLOG.error("OpenStack get-token did not complete, "
"host_uuid=%s, host_name=%s." % (host_uuid,
host_name))
return
self._token = future.result.data
response['reason'] = 'failed to create guest services'
try:
# Send the create request to Guest.
future.work(guest.host_services_create,
self._token,
host_uuid, host_name)
future.result = (yield)
if not future.result.is_complete():
DLOG.error("Guest host-services-create failed, "
"operation did not complete, host_uuid=%s, "
"host_name=%s."
% (host_uuid, host_name))
return
response['reason'] = 'failed to disable guest services'
# Send the disable request to Guest
future.work(guest.host_services_disable,
self._token,
host_uuid, host_name)
future.result = (yield)
if not future.result.is_complete():
# do not return since the disable will be retried
# by audit
DLOG.error("Guest host-services-disable failed, "
"operation did not complete, host_uuid=%s, "
"host_name=%s."
% (host_uuid, host_name))
except exceptions.OpenStackRestAPIException as e:
# Guest can send a 404 if it hasn't got the host
# inventory yet.
# Guest will catch up later, no need to fail here.
if httplib.NOT_FOUND != e.http_status_code:
raise
response['completed'] = True
response['reason'] = ''
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 create "
"guest services on host, error=%s." % e)
except Exception as e:
DLOG.exception("Caught exception while trying to create %s "
"guest services, error=%s." % (host_name, e))
finally:
callback.send(response)
callback.close()
def delete_host_services(self, future, host_uuid, host_name,
host_personality, callback):
"""
Delete Host Services, notify Guest to delete services for a host
"""
response = dict()
response['completed'] = False
response['reason'] = ''
try:
future.set_timeouts(config.CONF.get('nfvi-timeouts', None))
if self._host_supports_nova_compute(host_personality):
response['reason'] = 'failed to get platform token from ' \
'keystone'
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() or \
future.result.data is None:
DLOG.error("OpenStack get-token did not complete, "
"host_uuid=%s, host_name=%s." % (host_uuid,
host_name))
return
self._token = future.result.data
response['reason'] = 'failed to delete guest services'
# Send the delete request to Guest.
future.work(guest.host_services_delete, self._token,
host_uuid)
try:
future.result = (yield)
if not future.result.is_complete():
DLOG.error("Guest host-services-delete for host "
"failed, operation did not complete, "
"host_uuid=%s, host_name=%s."
% (host_uuid, host_name))
return
except exceptions.OpenStackRestAPIException as e:
if httplib.NOT_FOUND != e.http_status_code:
raise
response['completed'] = True
response['reason'] = ''
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 delete "
"host services on host, error=%s." % e)
except Exception as e:
DLOG.exception("Caught exception while trying to delete %s "
"guest services, error=%s."
% (host_name, e))
finally:
callback.send(response)
callback.close()
def enable_host_services(self, future, host_uuid, host_name,
host_personality, callback):
"""
Enable Host Services, notify Guest to enable services for a host.
"""
response = dict()
response['completed'] = False
response['reason'] = ''
try:
future.set_timeouts(config.CONF.get('nfvi-timeouts', None))
if self._host_supports_nova_compute(host_personality):
response['reason'] = 'failed to get platform token from ' \
'keystone'
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() or \
future.result.data is None:
DLOG.error("OpenStack get-token did not complete, "
"host_uuid=%s, host_name=%s." % (host_uuid,
host_name))
return
self._token = future.result.data
response['reason'] = 'failed to enable guest services'
# Send the Enable request to Guest
future.work(guest.host_services_enable, self._token,
host_uuid, host_name)
future.result = (yield)
if not future.result.is_complete():
DLOG.error("Guest host-services-enable failed, operation "
"did not complete, host_uuid=%s, host_name=%s."
% (host_uuid, host_name))
return
response['completed'] = True
response['reason'] = ''
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 enable "
"guest services on host, error=%s." % e)
except Exception as e:
DLOG.exception("Caught exception while trying to enable %s "
"guest services, error=%s."
% (host_name, e))
finally:
callback.send(response)
callback.close()
def disable_host_services(self, future, host_uuid, host_name,
host_personality, callback):
"""
Notifies Guest to disable their services for the specified
host (as applicable)
"""
response = dict()
response['completed'] = False
response['reason'] = ''
try:
future.set_timeouts(config.CONF.get('nfvi-timeouts', None))
# The following only applies to worker hosts
if self._host_supports_nova_compute(host_personality):
response['reason'] = 'failed to get platform token from ' \
'keystone'
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() or \
future.result.data is None:
DLOG.error("OpenStack get-token did not complete, "
"host_uuid=%s, host_name=%s." % (host_uuid,
host_name))
return
self._token = future.result.data
response['reason'] = 'failed to disable guest services'
# Send the Disable request to Guest.
future.work(guest.host_services_disable, self._token,
host_uuid, host_name)
try:
future.result = (yield)
if not future.result.is_complete():
# Do not return since the disable will be retried
# by audit
DLOG.error("Guest host-services-disable failed, "
"operation did not complete, host_uuid=%s, "
"host_name=%s."
% (host_uuid, host_name))
except exceptions.OpenStackRestAPIException as e:
if httplib.NOT_FOUND != e.http_status_code:
raise
response['completed'] = True
response['reason'] = ''
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 disable "
"guest services, error=%s." % e)
except Exception as e:
DLOG.exception("Caught exception while trying to disable %s "
"guest services, error=%s."
% (host_name, e))
finally:
callback.send(response)
callback.close()
def query_host_services(self, future, host_uuid, host_name,
host_personality, callback):
"""
Query Host Services, return state of Guest services for a host.
"""
response = dict()
response['completed'] = False
response['result-data'] = 'enabled'
response['reason'] = ''
try:
future.set_timeouts(config.CONF.get('nfvi-timeouts', None))
if self._host_supports_nova_compute(host_personality):
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() or \
future.result.data is None:
DLOG.error("OpenStack get-token did not complete, "
"host_uuid=%s, host_name=%s." % (host_uuid,
host_name))
return
self._token = future.result.data
# Send Query request to Guest
future.work(guest.host_services_query, self._token,
host_uuid, host_name)
future.result = (yield)
if not future.result.is_complete():
DLOG.error("Guest query-host-services failed, operation "
"did not complete, host_uuid=%s, host_name=%s."
% (host_uuid, host_name))
else:
result_data = future.result.data
response['result-data'] = result_data['state']
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 query "
"host services, error=%s." % e)
except Exception as e:
DLOG.exception("Caught exception while trying to query %s "
"nova or neutron openstack services, error=%s."
% (host_name, e))
finally:
callback.send(response)
callback.close()
def host_services_rest_api_get_handler(self, request_dispatch):
"""
Host-Services Rest-API GET handler
"""
content_len = int(request_dispatch.headers.get('content-length', 0))
content = request_dispatch.rfile.read(content_len)
DLOG.info("Content=%s, len=%s" % (content, content_len))
http_payload = None
http_response = httplib.OK
if content:
host_data = json.loads(content)
if host_data['uuid'] is None:
DLOG.error("Invalid host uuid received")
http_response = httplib.BAD_REQUEST
elif host_data['hostname'] is None:
DLOG.info("Invalid host name received")
http_response = httplib.BAD_REQUEST
else:
(success, host_state) = \
self._host_services_query_callback(host_data['hostname'])
if not success:
http_response = httplib.BAD_REQUEST
else:
http_payload = dict()
http_payload['uuid'] = host_data['uuid']
http_payload['hostname'] = host_data['hostname']
http_payload['state'] = host_state
else:
http_response = httplib.NO_CONTENT
DLOG.debug("Host rest-api get path: %s." % request_dispatch.path)
request_dispatch.send_response(http_response)
if http_payload is not None:
request_dispatch.send_header('Content-Type', 'application/json')
request_dispatch.end_headers()
request_dispatch.wfile.write(json.dumps(http_payload).encode())
request_dispatch.done()
def guest_services_rest_api_get_handler(self, request_dispatch):
"""
Guest-Services Rest-API GET handler
"""
# /nfvi-plugins/v1/instances/<instance-uuid>
# /nfvi-plugins/v1/instances/?host_uuid=<host-uuid>
host_uuid = None
instance_uuid = None
http_payload = None
path = request_dispatch.path
host_prefix = "host_uuid"
if host_prefix in path:
host_uuid = path.split('=')[-1]
else:
instance_uuid = path.split('/')[-1]
DLOG.debug("Guest-Services rest-api path=%s, host_uuid=%s, "
"instance_uuid=%s" % (path, host_uuid, instance_uuid))
http_response = httplib.OK
(success, result) = \
self._guest_services_query_callback(host_uuid, instance_uuid)
if not success:
http_response = httplib.BAD_REQUEST
else:
if instance_uuid:
result['services'] = \
get_services_with_guest_service_state(result['services'])
else:
for r in result['instances']:
r['services'] = \
get_services_with_guest_service_state(r['services'])
http_payload = result
DLOG.debug("Guest-Services rest-api get path: %s." %
request_dispatch.path)
request_dispatch.send_response(http_response)
if http_payload is not None:
request_dispatch.send_header('Content-Type', 'application/json')
request_dispatch.end_headers()
request_dispatch.wfile.write(json.dumps(http_payload).encode())
request_dispatch.done()
def guest_services_rest_api_patch_handler(self, request_dispatch):
"""
Guest-Services Rest-API PATCH handler callback
"""
content_len = int(request_dispatch.headers.get('content-length', 0))
content = request_dispatch.rfile.read(content_len)
http_payload = None
http_response = httplib.OK
if content:
instance_data = json.loads(content)
instance_uuid = instance_data.get('uuid', None)
host_name = instance_data.get('hostname', None)
event_type = instance_data.get('event-type', None)
event_data = instance_data.get('event-data', None)
result = None
if instance_uuid is None:
DLOG.info("Invalid instance uuid received")
http_response = httplib.BAD_REQUEST
elif event_type is None:
DLOG.info("Invalid event-type received")
http_response = httplib.BAD_REQUEST
elif event_data is None:
DLOG.info("Invalid event-data received")
http_response = httplib.BAD_REQUEST
else:
if 'service' == event_type:
services = event_data.get('services', list())
service_objs = list()
for service in services:
restart_timeout = service.get('restart-timeout', None)
if restart_timeout is not None:
restart_timeout = int(restart_timeout)
service_obj = nfvi.objects.v1.GuestService(
guest_service_get_name(service['service']),
guest_service_get_admin_state(service['state']),
guest_service_get_oper_state(service['status']),
restart_timeout)
service_objs.append(service_obj)
for callback in self._guest_services_state_notify_callbacks:
callback(instance_uuid, host_name, service_objs)
elif 'alarm' == event_type:
services = event_data.get('services', list())
for service_data in services:
if guest.GUEST_SERVICE_NAME.HEARTBEAT == \
service_data['service']:
avail_status = service_data.get('state', None)
recovery_action = \
service_data.get('repair-action', None)
if 'reboot' == recovery_action:
recovery_action = \
nfvi.objects.v1.INSTANCE_ACTION_TYPE.REBOOT
elif 'stop' == recovery_action:
recovery_action = \
nfvi.objects.v1.INSTANCE_ACTION_TYPE.STOP
elif 'log' == recovery_action:
recovery_action = \
nfvi.objects.v1.INSTANCE_ACTION_TYPE.LOG
if 'failed' == avail_status:
avail_status = \
nfvi.objects.v1.INSTANCE_AVAIL_STATUS.FAILED
elif 'unhealthy' == avail_status:
avail_status = \
nfvi.objects.v1.INSTANCE_AVAIL_STATUS.UNHEALTHY
for callback in \
self._guest_services_alarm_notify_callbacks:
(success, result) = callback(instance_uuid,
avail_status,
recovery_action)
if not success:
DLOG.error("Callback failed for "
"instance_uuid=%s" % instance_uuid)
http_response = httplib.BAD_REQUEST
elif 'action' == event_type:
action_type = \
instance_get_action_type(event_data.get('action'))
guest_response = event_data.get('guest-response')
if guest.GUEST_VOTE_STATE.REJECT == guest_response:
action_state = \
nfvi.objects.v1.INSTANCE_ACTION_STATE.REJECTED
elif guest.GUEST_VOTE_STATE.ALLOW == guest_response:
action_state = \
nfvi.objects.v1.INSTANCE_ACTION_STATE.ALLOWED
elif guest.GUEST_VOTE_STATE.PROCEED == guest_response:
action_state = \
nfvi.objects.v1.INSTANCE_ACTION_STATE.PROCEED
else:
action_state = guest.GUEST_VOTE_STATE.PROCEED
DLOG.info("Invalid guest-response received, "
"defaulting to proceed, response=%s."
% guest_response)
reason = event_data.get('reason')
for callback in \
self._guest_services_action_notify_callbacks:
result = None
success = callback(instance_uuid, action_type,
action_state, reason)
if not success:
DLOG.error("callback failed for instance_uuid=%s"
% instance_uuid)
http_response = httplib.BAD_REQUEST
else:
DLOG.info("Invalid event-type %s received" % event_type)
http_response = httplib.BAD_REQUEST
if httplib.OK == http_response:
http_payload = result
else:
http_response = httplib.NO_CONTENT
DLOG.debug("Guest-Services rest-api patch path: %s."
% request_dispatch.path)
request_dispatch.send_response(http_response)
if http_payload is not None:
request_dispatch.send_header('Content-Type', 'application/json')
request_dispatch.end_headers()
request_dispatch.wfile.write(json.dumps(http_payload).encode())
request_dispatch.done()
def register_host_services_query_callback(self, callback):
"""
Register for Host Services query
"""
self._host_services_query_callback = callback
def register_guest_services_query_callback(self, callback):
"""
Register for Guest Services query
"""
self._guest_services_query_callback = callback
def register_guest_services_state_notify_callback(self, callback):
"""
Register for Guest Services notify service type event
"""
self._guest_services_state_notify_callbacks.append(callback)
def register_guest_services_alarm_notify_callback(self, callback):
"""
Register for Guest Services notify for alarm type event
"""
self._guest_services_alarm_notify_callbacks.append(callback)
def register_guest_services_action_notify_callback(self, callback):
"""
Register for Guest Services notify for action type event
"""
self._guest_services_action_notify_callbacks.append(callback)
def initialize(self, config_file):
"""
Initialize the plugin
"""
config.load(config_file)
self._directory = openstack.get_directory(
config, openstack.SERVICE_CATEGORY.PLATFORM)
self._openstack_directory = openstack.get_directory(
config, openstack.SERVICE_CATEGORY.OPENSTACK)
self._rest_api_server = rest_api.rest_api_get_server(
config.CONF['guest-rest-api']['host'],
config.CONF['guest-rest-api']['port'])
self._rest_api_server.add_handler(
'GET', '/nfvi-plugins/v1/hosts*',
self.host_services_rest_api_get_handler)
self._rest_api_server.add_handler(
'GET', '/nfvi-plugins/v1/instances*',
self.guest_services_rest_api_get_handler)
self._rest_api_server.add_handler(
'PATCH', '/nfvi-plugins/v1/instances*',
self.guest_services_rest_api_patch_handler)
def finalize(self):
"""
Finalize the plugin
"""
return