From b53a3ff0eacb0972907926000377f619af6b37f6 Mon Sep 17 00:00:00 2001 From: Scott Hussey Date: Fri, 6 Jul 2018 15:38:35 -0500 Subject: [PATCH] Add support of MAAS node-results API - Support extraction of log data from MAAS Change-Id: I55fcbad5e06378f148c9d5de476e1eef8d898ed2 --- .../drivers/node/maasdriver/models/base.py | 8 +- .../drivers/node/maasdriver/models/machine.py | 12 +++ .../node/maasdriver/models/node_results.py | 93 +++++++++++++++++++ tests/unit/test_maasdriver_noderesults.py | 55 +++++++++++ 4 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 drydock_provisioner/drivers/node/maasdriver/models/node_results.py create mode 100644 tests/unit/test_maasdriver_noderesults.py diff --git a/drydock_provisioner/drivers/node/maasdriver/models/base.py b/drydock_provisioner/drivers/node/maasdriver/models/base.py index b4c53ef3..70ce79a1 100644 --- a/drydock_provisioner/drivers/node/maasdriver/models/base.py +++ b/drydock_provisioner/drivers/node/maasdriver/models/base.py @@ -303,9 +303,9 @@ class ResourceCollectionBase(object): """Iterate over the resources in the collection.""" return iter(self.resources.values()) - """ - Resource count - """ - def len(self): + return len(self) + + def __len__(self): + """Resource count.""" return len(self.resources) diff --git a/drydock_provisioner/drivers/node/maasdriver/models/machine.py b/drydock_provisioner/drivers/node/maasdriver/models/machine.py index 2e0a5eb1..7493c08e 100644 --- a/drydock_provisioner/drivers/node/maasdriver/models/machine.py +++ b/drydock_provisioner/drivers/node/maasdriver/models/machine.py @@ -20,6 +20,7 @@ import drydock_provisioner.drivers.node.maasdriver.models.base as model_base import drydock_provisioner.drivers.node.maasdriver.models.interface as maas_interface import drydock_provisioner.drivers.node.maasdriver.models.blockdev as maas_blockdev import drydock_provisioner.drivers.node.maasdriver.models.volumegroup as maas_vg +import drydock_provisioner.drivers.node.maasdriver.models.node_results as maas_nr from bson import BSON @@ -296,6 +297,17 @@ class Machine(model_base.ResourceBase): detail_config = BSON.decode(resp.content) return detail_config + def get_task_results(self, result_type='all'): + """Get the result from tasks run during node deployment. + + :param str result_type: the type of results to return. One of + ``all``, ``commissioning``, ``testing``, ``deploy`` + """ + node_results = maas_nr.NodeResults(system_id_list=[self.resource_id], result_type=result_type) + node_results.refresh() + + return node_results + def set_owner_data(self, key, value): """Add/update/remove node owner data. diff --git a/drydock_provisioner/drivers/node/maasdriver/models/node_results.py b/drydock_provisioner/drivers/node/maasdriver/models/node_results.py new file mode 100644 index 00000000..162b35af --- /dev/null +++ b/drydock_provisioner/drivers/node/maasdriver/models/node_results.py @@ -0,0 +1,93 @@ +# Copyright 2018 AT&T Intellectual Property. All other 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. +"""Model for MaaS Node Result resources.""" +import base64 +import binascii + +import drydock_provisioner.drivers.node.maasdriver.models.base as model_base + + +class NodeResult(model_base.ResourceBase): + + resource_url = 'commissioning-results/' + fields = ['resource_id', 'name', 'result_type', 'updated', 'data', 'script_result'] + json_fields = [] + + type_map = { + 'commissioning': 0, + 'deploy': 1, + 'testing': 2, + } + + type_rev_map = { + 0: 'commissioning', + 1: 'deploy', + 2: 'testing', + } + + def __init__(self, api_client, **kwargs): + super().__init__(api_client, **kwargs) + + def refresh(self): + """Don't allow refresh of individual result.""" + return + + def update(self): + """Don't allow updates.""" + return + + def get_decoded_data(self): + """Decode the result data from base64.""" + try: + return base64.b64decode(self.data) + except binascii.Error as e: + return None + + def get_type_desc(self): + return NodeResult.type_rev_map.get(self.result_type) + +class NodeResults(model_base.ResourceCollectionBase): + + collection_url = 'commissioning-results/' + collection_resource = NodeResult + + def __init__(self, api_client, system_id_list=None, result_type=None): + super().__init__(api_client) + + self.system_id_list = system_id_list + self.result_type = result_type + + def refresh(self): + params = dict() + if self.system_id_list: + params['system_id'] = self.system_id_list + if self.result_type and self.result_type != 'all': + params['result_type'] = NodeResult.type_map.get(self.result_type) + + url = self.interpolate_url() + + if params: + resp = self.api_client.get(url, files=params) + else: + resp = self.api_client.get(url) + + if resp.status_code in [200]: + json_list = resp.json() + self.resource = dict() + + for o in json_list: + if isinstance(o, dict): + i = self.collection_resource.from_dict(self.api_client, o) + self.resources[i.resource_id] = i + return diff --git a/tests/unit/test_maasdriver_noderesults.py b/tests/unit/test_maasdriver_noderesults.py new file mode 100644 index 00000000..954a7e2a --- /dev/null +++ b/tests/unit/test_maasdriver_noderesults.py @@ -0,0 +1,55 @@ +# Copyright 2018 AT&T Intellectual Property. All other 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. +'''Tests for the maasdriver node_results routine.''' +from drydock_provisioner.drivers.node.maasdriver.models.node_results import NodeResults + + +class TestMaasNodeResults(): + def test_get_noderesults(self, mocker): + '''Test noderesults refresh call to load a list of NodeResults.''' + # A object to return that looks like a requests response + # object wrapping a MAAS API response + class MockedResponse(): + + status_code = 200 + + def json(self): + resp_content = [{ + "id": 3, + "data": "SGVsbG8gV29ybGQh", + "result_type": 0, + "script_result": 0, + "resource_uri": "/MAAS/api/2.0/commissioning-scripts/", + "updated": "2018-07-06T14:32:20.129", + "node": { + "system_id": "r7mqnw" + }, + "created": "2018-07-06T14:37:12.632", + "name": "hello_world" + }] + + return resp_content + + api_client = mocker.MagicMock() + api_client.get.return_value = MockedResponse() + + nr_list = NodeResults(api_client) + nr_list.refresh() + + api_client.get.assert_called_with('commissioning-results/') + assert len(nr_list) == 1 + + nr = nr_list.singleton({'name': 'hello_world'}) + + assert nr.get_decoded_data() == b'Hello World!'