Notify Nova when all ARQs are resolved for an instance.
Once all the ARQs for an instance have either bound or failed to bind, Cyborg sends a notification event to Nova indicating success or failure. Change-Id: I05cce190d328ae16738959ba806d13a5ae99053c
This commit is contained in:
parent
bc2483960c
commit
fdeceed353
1
.gitignore
vendored
1
.gitignore
vendored
@ -19,6 +19,7 @@ doc/source/_static/cyborg.policy.yaml.sample
|
||||
etc/cyborg/policy.json.sample
|
||||
etc/cyborg/cyborg.conf.sample
|
||||
.idea/*
|
||||
.vscode/*
|
||||
|
||||
# Editors
|
||||
*.sublime-workspace
|
||||
|
59
cyborg/common/nova_client.py
Normal file
59
cyborg/common/nova_client.py
Normal file
@ -0,0 +1,59 @@
|
||||
# Copyright 2019 Intel, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from cyborg.conf import CONF
|
||||
from openstack import connection
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NovaAPI(object):
|
||||
def __init__(self):
|
||||
default_user = "devstack-admin"
|
||||
try:
|
||||
auth_user = CONF.compute.username
|
||||
except Exception:
|
||||
auth_user = default_user
|
||||
self.conn = connection.Connection(cloud=auth_user)
|
||||
self.nova_client = self.conn.compute
|
||||
|
||||
def _get_acc_changed_event(self, instance_uuid, dev_profile_name, status):
|
||||
return [{'name': 'accelerator-requests-bound',
|
||||
'server_uuid': instance_uuid,
|
||||
'tag': dev_profile_name,
|
||||
'status': status}
|
||||
]
|
||||
|
||||
def _send_events(self, events):
|
||||
url = "/os-server-external-events"
|
||||
body = {"events": events}
|
||||
response = self.nova_client.post(url, json=body)
|
||||
if response.ok:
|
||||
LOG.info("Sucessfully send events to Nova, events: %(events)s",
|
||||
{"events": events})
|
||||
return True
|
||||
else:
|
||||
raise Exception(
|
||||
"Failed to send events %s: HTTP %d: %s" %
|
||||
(events, response.status_code, response.text))
|
||||
return False
|
||||
|
||||
def notify_binding(self, instance_uuid, dev_profile_name, status):
|
||||
events = self._get_acc_changed_event(instance_uuid, dev_profile_name,
|
||||
status)
|
||||
result = self._send_events(events)
|
||||
if not result:
|
||||
LOG.error("Failed to notify Nova service.")
|
||||
return result
|
@ -22,6 +22,7 @@ from cyborg.agent.rpcapi import AgentAPI
|
||||
from cyborg.db import api as dbapi
|
||||
from cyborg.common import constants
|
||||
from cyborg.common import exception
|
||||
from cyborg.common import nova_client
|
||||
from cyborg.common import placement_client
|
||||
from cyborg import objects
|
||||
from cyborg.objects import base
|
||||
@ -306,15 +307,36 @@ class ExtARQ(base.CyborgObject, object_base.VersionedObjectDictCompat):
|
||||
@classmethod
|
||||
def apply_patch(cls, context, patch_list, valid_fields):
|
||||
"""Apply JSON patch. See api/controllers/v1/arqs.py. """
|
||||
device_profile_name = None
|
||||
instance_uuid = None
|
||||
bind_action = False
|
||||
status = "completed"
|
||||
for arq_uuid, patch in patch_list.items():
|
||||
extarq = ExtARQ.get(context, arq_uuid)
|
||||
if not device_profile_name:
|
||||
device_profile_name = extarq.arq.device_profile_name
|
||||
if not instance_uuid:
|
||||
instance_uuid = valid_fields[arq_uuid]['instance_uuid']
|
||||
if patch[0]['op'] == 'add': # All ops are 'add'
|
||||
# True if do binding, False if do unbinding.
|
||||
bind_action = True
|
||||
extarq.bind(context,
|
||||
valid_fields[arq_uuid]['hostname'],
|
||||
valid_fields[arq_uuid]['device_rp_uuid'],
|
||||
valid_fields[arq_uuid]['instance_uuid'])
|
||||
if extarq.arq.state == constants.ARQ_BIND_FAILED:
|
||||
status = "failed"
|
||||
elif extarq.arq.state == constants.ARQ_BOUND:
|
||||
continue
|
||||
else:
|
||||
raise exception.ARQInvalidState(state=extarq.arq.state)
|
||||
else:
|
||||
bind_action = False
|
||||
extarq.unbind(context)
|
||||
if bind_action:
|
||||
nova_api = nova_client.NovaAPI()
|
||||
nova_api.notify_binding(instance_uuid,
|
||||
device_profile_name, status)
|
||||
|
||||
def unbind(self, context):
|
||||
arq = self.arq
|
||||
|
@ -66,3 +66,11 @@ def get_fake_extarq_objs():
|
||||
arq_list = _get_arqs_as_dict()
|
||||
obj_extarqs = map(_convert_from_dict_to_obj, arq_list)
|
||||
return obj_extarqs
|
||||
|
||||
|
||||
def get_fake_db_extarqs():
|
||||
db_extarqs = []
|
||||
for db_extarq in _get_arqs_as_dict():
|
||||
db_extarq.update({'device_profile_id': 0})
|
||||
db_extarqs.append(db_extarq)
|
||||
return db_extarqs
|
||||
|
@ -1,90 +0,0 @@
|
||||
# Copyright 2019 Beijing Lenovo Software Ltd.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
|
||||
from testtools.matchers import HasLength
|
||||
from cyborg import objects
|
||||
from cyborg.tests.unit.db import base
|
||||
from cyborg.tests.unit.db import utils
|
||||
|
||||
|
||||
class TestExtARQObject(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestExtARQObject, self).setUp()
|
||||
self.fake_arq = utils.get_test_arq()
|
||||
|
||||
def test_get(self):
|
||||
uuid = self.fake_arq['uuid']
|
||||
with mock.patch.object(self.dbapi, 'extarq_get',
|
||||
autospec=True) as mock_extarq_get:
|
||||
mock_extarq_get.return_value = self.fake_arq
|
||||
extarq = objects.ExtARQ.get(self.context, uuid)
|
||||
mock_extarq_get.assert_called_once_with(self.context, uuid)
|
||||
self.assertEqual(self.context, extarq._context)
|
||||
|
||||
def test_list(self):
|
||||
with mock.patch.object(self.dbapi, 'extarq_list',
|
||||
autospec=True) as mock_get_list:
|
||||
mock_get_list.return_value = [self.fake_arq]
|
||||
extarqs = objects.ExtARQ.list(self.context, 1, None, None, None)
|
||||
self.assertEqual(1, mock_get_list.call_count)
|
||||
self.assertThat(extarqs, HasLength(1))
|
||||
self.assertIsInstance(extarqs[0], objects.ExtARQ)
|
||||
self.assertEqual(self.context, extarqs[0]._context)
|
||||
|
||||
def test_create(self):
|
||||
with mock.patch.object(self.dbapi, 'extarq_create',
|
||||
autospec=True) as mock_extarq_create:
|
||||
mock_extarq_create.return_value = self.fake_arq
|
||||
extarq = objects.ExtARQ(self.context, **self.fake_arq)
|
||||
extarq.arq = objects.ARQ(self.context, **self.fake_arq)
|
||||
extarq.create(self.context)
|
||||
mock_extarq_create.assert_called_once_with(self.context,
|
||||
self.fake_arq)
|
||||
self.assertEqual(self.context, extarq._context)
|
||||
|
||||
def test_destroy(self):
|
||||
uuid = self.fake_arq['uuid']
|
||||
with mock.patch.object(self.dbapi, 'extarq_get',
|
||||
autospec=True) as mock_extarq_get:
|
||||
mock_extarq_get.return_value = self.fake_arq
|
||||
with mock.patch.object(self.dbapi, 'extarq_delete',
|
||||
autospec=True) as mock_extarq_delete:
|
||||
extarq = objects.ExtARQ.get(self.context, uuid)
|
||||
extarq.destroy(self.context)
|
||||
mock_extarq_delete.assert_called_once_with(self.context, uuid)
|
||||
self.assertEqual(self.context, extarq._context)
|
||||
|
||||
def test_save(self):
|
||||
uuid = self.fake_arq['uuid']
|
||||
with mock.patch.object(self.dbapi, 'extarq_get',
|
||||
autospec=True) as mock_extarq_get:
|
||||
mock_extarq_get.return_value = self.fake_arq
|
||||
with mock.patch.object(self.dbapi, 'extarq_update',
|
||||
autospec=True) as mock_extarq_update:
|
||||
extarq = objects.ExtARQ.get(self.context, uuid)
|
||||
arq = extarq.arq
|
||||
arq.hostname = 'newtestnode1'
|
||||
fake_arq_updated = self.fake_arq
|
||||
fake_arq_updated['hostname'] = arq.hostname
|
||||
mock_extarq_update.return_value = fake_arq_updated
|
||||
extarq.save(self.context)
|
||||
mock_extarq_get.assert_called_once_with(self.context, uuid)
|
||||
mock_extarq_update.assert_called_once_with(
|
||||
self.context, uuid,
|
||||
{'hostname': 'newtestnode1'})
|
||||
self.assertEqual(self.context, extarq._context)
|
130
cyborg/tests/unit/objects/test_extarq.py
Normal file
130
cyborg/tests/unit/objects/test_extarq.py
Normal file
@ -0,0 +1,130 @@
|
||||
# Copyright 2019 Beijing Lenovo Software Ltd.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
|
||||
from testtools.matchers import HasLength
|
||||
from cyborg import objects
|
||||
from cyborg.tests.unit.db import base
|
||||
from cyborg.tests.unit.db import utils
|
||||
from cyborg.tests.unit import fake_extarq
|
||||
|
||||
|
||||
class TestExtARQObject(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestExtARQObject, self).setUp()
|
||||
self.fake_db_extarqs = fake_extarq.get_fake_db_extarqs()
|
||||
self.fake_obj_extarqs = fake_extarq.get_fake_extarq_objs()
|
||||
|
||||
@mock.patch('cyborg.objects.ExtARQ._from_db_object')
|
||||
def test_get(self, mock_from_db_obj):
|
||||
db_extarq = self.fake_db_extarqs[0]
|
||||
uuid = db_extarq['uuid']
|
||||
mock_from_db_obj.return_value = self.fake_obj_extarqs[0]
|
||||
with mock.patch.object(self.dbapi, 'extarq_get',
|
||||
autospec=True) as mock_extarq_get:
|
||||
mock_extarq_get.return_value = db_extarq
|
||||
obj_extarq = objects.ExtARQ.get(self.context, uuid)
|
||||
mock_extarq_get.assert_called_once_with(self.context, uuid)
|
||||
self.assertEqual(obj_extarq.arq.uuid, uuid)
|
||||
|
||||
@mock.patch('cyborg.objects.ExtARQ._from_db_object')
|
||||
def test_list(self, mock_from_db_obj):
|
||||
db_extarq = self.fake_db_extarqs[0]
|
||||
mock_from_db_obj.return_value = self.fake_obj_extarqs[0]
|
||||
with mock.patch.object(self.dbapi, 'extarq_list',
|
||||
autospec=True) as mock_get_list:
|
||||
mock_get_list.return_value = [db_extarq]
|
||||
obj_extarqs = objects.ExtARQ.list(self.context)
|
||||
self.assertEqual(1, mock_get_list.call_count)
|
||||
self.assertThat(obj_extarqs, HasLength(1))
|
||||
self.assertIsInstance(obj_extarqs[0], objects.ExtARQ)
|
||||
for obj_extarq in obj_extarqs:
|
||||
self.assertEqual(obj_extarqs[0].arq.uuid, db_extarq['uuid'])
|
||||
|
||||
@mock.patch('cyborg.objects.ExtARQ._from_db_object')
|
||||
def test_create(self, mock_from_db_obj):
|
||||
db_extarq = self.fake_db_extarqs[0]
|
||||
mock_from_db_obj.return_value = self.fake_obj_extarqs[0]
|
||||
with mock.patch.object(self.dbapi, 'extarq_create',
|
||||
autospec=True) as mock_extarq_create:
|
||||
mock_extarq_create.return_value = db_extarq
|
||||
extarq = objects.ExtARQ(self.context, **db_extarq)
|
||||
extarq.arq = objects.ARQ(self.context, **db_extarq)
|
||||
extarq.create(self.context)
|
||||
mock_extarq_create.assert_called_once()
|
||||
|
||||
@mock.patch('openstack.connection.Connection')
|
||||
@mock.patch('cyborg.common.nova_client.NovaAPI.notify_binding')
|
||||
@mock.patch('cyborg.objects.ExtARQ.bind')
|
||||
@mock.patch('cyborg.objects.ExtARQ.get')
|
||||
def test_apply_patch(self, mock_get, mock_bind, mock_notify_bind,
|
||||
mock_conn):
|
||||
mock_get.return_value = obj_extarq = self.fake_obj_extarqs[0]
|
||||
uuid = obj_extarq.arq.uuid
|
||||
instance_uuid = obj_extarq.arq.instance_uuid
|
||||
valid_fields = {
|
||||
uuid: {'hostname': obj_extarq.arq.hostname,
|
||||
'device_rp_uuid': obj_extarq.arq.device_rp_uuid,
|
||||
'instance_uuid': instance_uuid}
|
||||
}
|
||||
patch_list = {
|
||||
str(uuid) : [
|
||||
{"path": "/hostname", "op": "add",
|
||||
"value": obj_extarq.arq.hostname},
|
||||
{"path": "/device_rp_uuid", "op": "add",
|
||||
"value": obj_extarq.arq.device_rp_uuid},
|
||||
{"path": "/instance_uuid", "op": "add",
|
||||
"value": instance_uuid}
|
||||
]
|
||||
}
|
||||
objects.ExtARQ.apply_patch(self.context, patch_list, valid_fields)
|
||||
status = 'completed'
|
||||
mock_notify_bind.assert_called_once_with(
|
||||
instance_uuid, obj_extarq.arq.device_profile_name, status)
|
||||
|
||||
@mock.patch('cyborg.objects.ExtARQ.get')
|
||||
@mock.patch('cyborg.objects.ExtARQ._from_db_object')
|
||||
def test_destroy(self, mock_from_db_obj, mock_obj_extarq):
|
||||
db_extarq = self.fake_db_extarqs[0]
|
||||
uuid = db_extarq['uuid']
|
||||
mock_from_db_obj.return_value = db_extarq
|
||||
mock_obj_extarq.return_value = self.fake_obj_extarqs[0]
|
||||
with mock.patch.object(self.dbapi, 'extarq_get',
|
||||
autospec=True) as mock_extarq_get:
|
||||
mock_extarq_get.return_value = db_extarq
|
||||
with mock.patch.object(self.dbapi, 'extarq_delete',
|
||||
autospec=True) as mock_extarq_delete:
|
||||
extarq = objects.ExtARQ.get(self.context, uuid)
|
||||
extarq.destroy(self.context)
|
||||
mock_extarq_delete.assert_called_once_with(self.context, uuid)
|
||||
|
||||
@mock.patch('cyborg.objects.ExtARQ.get')
|
||||
@mock.patch('cyborg.objects.ExtARQ._from_db_object')
|
||||
def test_save(self, mock_from_db_obj, mock_obj_extarq):
|
||||
db_extarq = self.fake_db_extarqs[0]
|
||||
uuid = db_extarq['uuid']
|
||||
mock_from_db_obj.return_value = db_extarq
|
||||
mock_obj_extarq.return_value = self.fake_obj_extarqs[0]
|
||||
with mock.patch.object(self.dbapi, 'extarq_update',
|
||||
autospec=True) as mock_extarq_update:
|
||||
obj_extarq = objects.ExtARQ.get(self.context, uuid)
|
||||
obj_extarq.arq.hostname = 'newtestnode1'
|
||||
fake_arq_updated = db_extarq
|
||||
fake_arq_updated['hostname'] = obj_extarq.arq.hostname
|
||||
mock_extarq_update.return_value = fake_arq_updated
|
||||
obj_extarq.save(self.context)
|
||||
mock_extarq_update.assert_called_once()
|
Loading…
Reference in New Issue
Block a user