Files
python-vitrageclient/vitrageclient/tests/cli/test_topology_show.py
Sean McGinnis 993563f610 Use unittest.mock instead of third party mock
Now that we no longer support py27, we can use the standard library
unittest.mock module instead of the third party mock lib.

Change-Id: I564357caf5982a0db9be01c77e9e8c42bee932d6
Signed-off-by: Sean McGinnis <sean.mcginnis@gmail.com>
2020-04-18 11:59:50 -05:00

367 lines
16 KiB
Python

# Copyright 2017 Nokia
#
# 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 argparse import ArgumentParser
from argparse import ArgumentTypeError
from unittest import mock
import json
# noinspection PyPackageRequirements
# noinspection PyPackageRequirements
import six
from testtools import ExpectedException
from vitrageclient.common.formatters import DOTFormatter
from vitrageclient.common.formatters import GraphMLFormatter
from vitrageclient.tests.cli.base import CliTestCase
from vitrageclient.v1.cli.topology import TopologyShow
JSON_DATA = '''
{
"directed": true,
"graph": {},
"links": [
{
"key": "contains",
"source": 0,
"vitrage_is_deleted": false,
"relationship_type": "contains",
"target": 2
},
{
"key": "contains",
"source": 1,
"vitrage_is_deleted": false,
"relationship_type": "contains",
"target": 0
}
],
"multigraph": true,
"nodes": [
{
"id": "nova",
"vitrage_sample_timestamp": "2018-12-31T13:44:03Z",
"vitrage_datasource_name": "nova.zone",
"vitrage_operational_state": "OK",
"name": "nova",
"state": "available",
"update_timestamp": "2018-12-31T13:44:03Z",
"is_real_vitrage_id": true,
"vitrage_id": "05a19de3-e929-4730-ad81-10fa57dcfa0a",
"vitrage_aggregated_state": "AVAILABLE",
"vitrage_type": "nova.zone",
"vitrage_is_deleted": false,
"graph_index": 0,
"vitrage_category": "RESOURCE",
"vitrage_cached_id": "125f1d8c4451a6385cc2cfa2b0ba45be",
"vitrage_is_placeholder": false
},
{
"id": "OpenStack Cluster",
"vitrage_sample_timestamp": "2018-12-31T13:44:03Z",
"vitrage_operational_state": "OK",
"name": "openstack.cluster",
"state": "available",
"graph_index": 1,
"vitrage_id": "070c413e-5a8c-4823-ae20-af44936de2a0",
"vitrage_aggregated_state": "AVAILABLE",
"vitrage_type": "openstack.cluster",
"is_real_vitrage_id": true,
"vitrage_category": "RESOURCE",
"vitrage_cached_id": "3c7f9d22d9dd1615a00404f86cb3e289",
"vitrage_is_deleted": false,
"vitrage_is_placeholder": false
},
{
"id": "ebarilan-devstack",
"vitrage_sample_timestamp": "2018-12-31T13:44:03Z",
"vitrage_datasource_name": "nova.host",
"vitrage_operational_state": "OK",
"name": "ebarilan-devstack",
"state": "available",
"update_timestamp": "2018-12-31T13:44:03Z",
"is_real_vitrage_id": true,
"vitrage_id": "10da4fa2-397f-4b2e-a43b-937e11ab7daf",
"vitrage_aggregated_state": "AVAILABLE",
"vitrage_type": "nova.host",
"vitrage_is_deleted": false,
"graph_index": 2,
"vitrage_category": "RESOURCE",
"vitrage_cached_id": "9ae4db6fb920e19cb5c57a428b29eb59",
"vitrage_is_placeholder": false
},
{
"id": "b36b4d7a-b309-4b02-9662-5abd79741750",
"vitrage_sample_timestamp": "2018-12-31T13:44:04Z",
"vitrage_datasource_name": "cinder.volume",
"project_id": "210140f1f5a94af99e0adf79a883b75a",
"vitrage_operational_state": "OK",
"vitrage_aggregated_state": "AVAILABLE",
"vitrage_is_placeholder": false,
"state": "available",
"attachments": [],
"graph_index": 3,
"vitrage_id": "f0ca9fac-3ebd-4748-97ba-e93a7e7108aa",
"size": 1,
"vitrage_type": "cinder.volume",
"vitrage_is_deleted": false,
"vitrage_category": "RESOURCE",
"vitrage_cached_id": "f998c5f7bf1851e17e3eea902800a7df",
"update_timestamp": "2018-12-31T08:43:32Z",
"is_real_vitrage_id": true,
"volume_type": "lvmdriver-1"
},
{
"id": "cebf5d5b-d7b1-4cfb-86fa-f660306b4c1a",
"vitrage_sample_timestamp": "2018-12-31T13:44:04Z",
"vitrage_datasource_name": "neutron.network",
"project_id": "210140f1f5a94af99e0adf79a883b75a",
"vitrage_operational_state": "OK",
"vitrage_category": "RESOURCE",
"vitrage_is_placeholder": false,
"state": "ACTIVE",
"update_timestamp": "2018-12-30T08:30:33Z",
"is_real_vitrage_id": true,
"vitrage_id": "eea46e33-81dc-4430-a771-852bac37b43d",
"vitrage_aggregated_state": "ACTIVE",
"vitrage_type": "neutron.network",
"vitrage_is_deleted": false,
"graph_index": 4,
"name": "public",
"vitrage_cached_id": "a0eeca0ab2c865915e23319a2e6d0fd7"
}
],
"raw": true
}
'''
DOT_DATA = '''\
strict digraph {
0 [id=nova, is_real_vitrage_id=True, label="nova\\nnova.zone", state=available, update_timestamp="2018-12-31T13:44:03Z", vitrage_aggregated_state=AVAILABLE, vitrage_cached_id="125f1d8c4451a6385cc2cfa2b0ba45be", vitrage_category=RESOURCE, vitrage_datasource_name="nova.zone", vitrage_id="05a19de3-e929-4730-ad81-10fa57dcfa0a", vitrage_is_deleted=False, vitrage_is_placeholder=False, vitrage_operational_state=OK, vitrage_sample_timestamp="2018-12-31T13:44:03Z", vitrage_type="nova.zone"];
1 [id="OpenStack Cluster", is_real_vitrage_id=True, label="openstack.cluster", state=available, vitrage_aggregated_state=AVAILABLE, vitrage_cached_id="3c7f9d22d9dd1615a00404f86cb3e289", vitrage_category=RESOURCE, vitrage_id="070c413e-5a8c-4823-ae20-af44936de2a0", vitrage_is_deleted=False, vitrage_is_placeholder=False, vitrage_operational_state=OK, vitrage_sample_timestamp="2018-12-31T13:44:03Z", vitrage_type="openstack.cluster"];
2 [id="ebarilan-devstack", is_real_vitrage_id=True, label="ebarilan-devstack\\nnova.host", state=available, update_timestamp="2018-12-31T13:44:03Z", vitrage_aggregated_state=AVAILABLE, vitrage_cached_id="9ae4db6fb920e19cb5c57a428b29eb59", vitrage_category=RESOURCE, vitrage_datasource_name="nova.host", vitrage_id="10da4fa2-397f-4b2e-a43b-937e11ab7daf", vitrage_is_deleted=False, vitrage_is_placeholder=False, vitrage_operational_state=OK, vitrage_sample_timestamp="2018-12-31T13:44:03Z", vitrage_type="nova.host"];
3 [attachments="[]", id="b36b4d7a-b309-4b02-9662-5abd79741750", is_real_vitrage_id=True, label="cinder.volume", project_id="210140f1f5a94af99e0adf79a883b75a", size=1, state=available, update_timestamp="2018-12-31T08:43:32Z", vitrage_aggregated_state=AVAILABLE, vitrage_cached_id=f998c5f7bf1851e17e3eea902800a7df, vitrage_category=RESOURCE, vitrage_datasource_name="cinder.volume", vitrage_id="f0ca9fac-3ebd-4748-97ba-e93a7e7108aa", vitrage_is_deleted=False, vitrage_is_placeholder=False, vitrage_operational_state=OK, vitrage_sample_timestamp="2018-12-31T13:44:04Z", vitrage_type="cinder.volume", volume_type="lvmdriver-1"];
4 [id="cebf5d5b-d7b1-4cfb-86fa-f660306b4c1a", is_real_vitrage_id=True, label="public\\nneutron.network", project_id="210140f1f5a94af99e0adf79a883b75a", state=ACTIVE, update_timestamp="2018-12-30T08:30:33Z", vitrage_aggregated_state=ACTIVE, vitrage_cached_id=a0eeca0ab2c865915e23319a2e6d0fd7, vitrage_category=RESOURCE, vitrage_datasource_name="neutron.network", vitrage_id="eea46e33-81dc-4430-a771-852bac37b43d", vitrage_is_deleted=False, vitrage_is_placeholder=False, vitrage_operational_state=OK, vitrage_sample_timestamp="2018-12-31T13:44:04Z", vitrage_type="neutron.network"];
0 -> 2 [label=contains, vitrage_is_deleted=False];
1 -> 0 [label=contains, vitrage_is_deleted=False];
}
''' # noqa
GRAPHML_DATA = u'''\
<?xml version='1.0' encoding='utf-8'?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<key attr.name="size" attr.type="int" for="node" id="d20" />
<key attr.name="project_id" attr.type="string" for="node" id="d19" />
<key attr.name="volume_type" attr.type="string" for="node" id="d18" />
<key attr.name="attachments" attr.type="string" for="node" id="d17" />
<key attr.name="label" attr.type="string" for="edge" id="d16" />
<key attr.name="vitrage_is_deleted" attr.type="boolean" for="edge" id="d15" />
<key attr.name="is_real_vitrage_id" attr.type="boolean" for="node" id="d14" />
<key attr.name="id" attr.type="string" for="node" id="d13" />
<key attr.name="label" attr.type="string" for="node" id="d12" />
<key attr.name="vitrage_is_placeholder" attr.type="boolean" for="node" id="d11" />
<key attr.name="vitrage_aggregated_state" attr.type="string" for="node" id="d10" />
<key attr.name="vitrage_sample_timestamp" attr.type="string" for="node" id="d9" />
<key attr.name="vitrage_type" attr.type="string" for="node" id="d8" />
<key attr.name="vitrage_cached_id" attr.type="string" for="node" id="d7" />
<key attr.name="state" attr.type="string" for="node" id="d6" />
<key attr.name="vitrage_operational_state" attr.type="string" for="node" id="d5" />
<key attr.name="vitrage_datasource_name" attr.type="string" for="node" id="d4" />
<key attr.name="vitrage_category" attr.type="string" for="node" id="d3" />
<key attr.name="update_timestamp" attr.type="string" for="node" id="d2" />
<key attr.name="vitrage_is_deleted" attr.type="boolean" for="node" id="d1" />
<key attr.name="vitrage_id" attr.type="string" for="node" id="d0" />
<graph edgedefault="directed">
<node id="0">
<data key="d0">05a19de3-e929-4730-ad81-10fa57dcfa0a</data>
<data key="d1">False</data>
<data key="d2">2018-12-31T13:44:03Z</data>
<data key="d3">RESOURCE</data>
<data key="d4">nova.zone</data>
<data key="d5">OK</data>
<data key="d6">available</data>
<data key="d7">125f1d8c4451a6385cc2cfa2b0ba45be</data>
<data key="d8">nova.zone</data>
<data key="d9">2018-12-31T13:44:03Z</data>
<data key="d10">AVAILABLE</data>
<data key="d11">False</data>
<data key="d12">nova
nova.zone</data>
<data key="d13">nova</data>
<data key="d14">True</data>
</node>
<node id="1">
<data key="d0">070c413e-5a8c-4823-ae20-af44936de2a0</data>
<data key="d1">False</data>
<data key="d3">RESOURCE</data>
<data key="d12">openstack.cluster</data>
<data key="d5">OK</data>
<data key="d6">available</data>
<data key="d7">3c7f9d22d9dd1615a00404f86cb3e289</data>
<data key="d8">openstack.cluster</data>
<data key="d9">2018-12-31T13:44:03Z</data>
<data key="d10">AVAILABLE</data>
<data key="d11">False</data>
<data key="d13">OpenStack Cluster</data>
<data key="d14">True</data>
</node>
<node id="2">
<data key="d0">10da4fa2-397f-4b2e-a43b-937e11ab7daf</data>
<data key="d1">False</data>
<data key="d2">2018-12-31T13:44:03Z</data>
<data key="d3">RESOURCE</data>
<data key="d4">nova.host</data>
<data key="d5">OK</data>
<data key="d6">available</data>
<data key="d7">9ae4db6fb920e19cb5c57a428b29eb59</data>
<data key="d8">nova.host</data>
<data key="d9">2018-12-31T13:44:03Z</data>
<data key="d10">AVAILABLE</data>
<data key="d11">False</data>
<data key="d12">ebarilan-devstack
nova.host</data>
<data key="d13">ebarilan-devstack</data>
<data key="d14">True</data>
</node>
<node id="3">
<data key="d0">f0ca9fac-3ebd-4748-97ba-e93a7e7108aa</data>
<data key="d1">False</data>
<data key="d2">2018-12-31T08:43:32Z</data>
<data key="d17">[]</data>
<data key="d3">RESOURCE</data>
<data key="d18">lvmdriver-1</data>
<data key="d4">cinder.volume</data>
<data key="d5">OK</data>
<data key="d6">available</data>
<data key="d7">f998c5f7bf1851e17e3eea902800a7df</data>
<data key="d8">cinder.volume</data>
<data key="d9">2018-12-31T13:44:04Z</data>
<data key="d12">cinder.volume</data>
<data key="d10">AVAILABLE</data>
<data key="d11">False</data>
<data key="d19">210140f1f5a94af99e0adf79a883b75a</data>
<data key="d13">b36b4d7a-b309-4b02-9662-5abd79741750</data>
<data key="d14">True</data>
<data key="d20">1</data>
</node>
<node id="4">
<data key="d0">eea46e33-81dc-4430-a771-852bac37b43d</data>
<data key="d1">False</data>
<data key="d2">2018-12-30T08:30:33Z</data>
<data key="d3">RESOURCE</data>
<data key="d4">neutron.network</data>
<data key="d5">OK</data>
<data key="d6">ACTIVE</data>
<data key="d7">a0eeca0ab2c865915e23319a2e6d0fd7</data>
<data key="d8">neutron.network</data>
<data key="d9">2018-12-31T13:44:04Z</data>
<data key="d12">public
neutron.network</data>
<data key="d10">ACTIVE</data>
<data key="d11">False</data>
<data key="d19">210140f1f5a94af99e0adf79a883b75a</data>
<data key="d13">cebf5d5b-d7b1-4cfb-86fa-f660306b4c1a</data>
<data key="d14">True</data>
</node>
<edge source="0" target="2">
<data key="d15">False</data>
<data key="d16">contains</data>
</edge>
<edge source="1" target="0">
<data key="d15">False</data>
<data key="d16">contains</data>
</edge>
</graph>
</graphml>
''' # noqa
# noinspection PyAttributeOutsideInit
class TopologyShowTest(CliTestCase):
def setUp(self):
super(TopologyShowTest, self).setUp()
self.topology_show = TopologyShow(mock.Mock(), mock.Mock())
def test_positive_integer_validation_with_negative(self):
self.assertRaises(ArgumentTypeError,
self.topology_show.positive_non_zero_int, -1)
def test_positive_integer_validation_with_zero(self):
self.assertRaises(ArgumentTypeError,
self.topology_show.positive_non_zero_int, 0)
def test_positive_integer_validation_with_string(self):
self.assertRaises(ArgumentTypeError,
self.topology_show.positive_non_zero_int, 'bla')
def test_positive_integer_validation_with_positive(self):
self.topology_show.positive_non_zero_int(1)
@mock.patch.object(ArgumentParser, "error")
def test_parser_topology_limit_with_a_negative_number(self, mock_parser):
mock_parser.side_effect = self._my_parser_error_func
parser = self.topology_show.get_parser('vitrage topology show')
with ExpectedException(ArgumentTypeError,
'argument --limit: -5 must be greater than 0'):
parser.parse_args(args=['--filter', 'bla',
'--limit', '-5',
'--root', 'blabla',
'--graph-type', 'tree'])
@mock.patch.object(ArgumentParser, "error")
def test_parser_topology_limit_with_a_string(self, mock_parser):
mock_parser.side_effect = self._my_parser_error_func
parser = self.topology_show.get_parser('vitrage topology show')
with ExpectedException(ArgumentTypeError,
'argument --limit: spam must be an integer'):
parser.parse_args(args=['--filter', 'bla',
'--limit', 'spam',
'--root', 'blabla',
'--graph-type', 'tree'])
def test_dot_emitter(self):
def dict2columns(data):
return zip(*sorted(data.items()))
out = six.StringIO()
formatter = DOTFormatter()
topology = json.loads(JSON_DATA)
columns, topology = dict2columns(topology)
formatter.emit_one(columns, topology, out)
self.assertEqual(DOT_DATA, out.getvalue())
def test_graphml_emitter(self):
def dict2columns(data):
return zip(*sorted(data.items()))
out = six.BytesIO()
formatter = GraphMLFormatter()
topology = json.loads(JSON_DATA)
columns, topology = dict2columns(topology)
formatter.emit_one(columns, topology, out)
actual = out.getvalue().decode('utf-8') # noqa
# skipping this. graphml keeps changing the xml file
# from test to test I cannot compare
# for now I will just check that it can parse and output
# and xml file
# self.assertEqual(GRAPHML_DATA, actual)