From 0e5b3ae1c894ea475c50eeb1f4e8316559b4ad34 Mon Sep 17 00:00:00 2001 From: Ricardo Branco Date: Fri, 3 Nov 2023 12:27:30 +0100 Subject: [PATCH] Use dataclass for Instance --- Makefile | 2 +- cloudview/__init__.py | 1 + cloudview/cloudview.py | 9 ++-- cloudview/ec2.py | 2 +- cloudview/gce.py | 2 +- cloudview/instance.py | 22 +++++--- cloudview/openstack.py | 2 +- tests/test_ec2.py | 5 ++ tests/test_instance.py | 112 +++++++++++++++++++++++++++++++++++------ tests/test_output.py | 13 ++++- 10 files changed, 135 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index 0981b94..c2fc1ad 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ flake8: .PHONY: pylint pylint: - @pylint $(FILES) + @pylint --disable=duplicate-code $(FILES) .PHONY: test test: diff --git a/cloudview/__init__.py b/cloudview/__init__.py index b6ca186..815ddf9 100644 --- a/cloudview/__init__.py +++ b/cloudview/__init__.py @@ -1,6 +1,7 @@ """ cloudview """ + __author__ = """Ricardo Branco""" __email__ = "rbranco@suse.de" __version__ = "0.9.7" diff --git a/cloudview/cloudview.py b/cloudview/cloudview.py index 3bf1050..a8c64c2 100644 --- a/cloudview/cloudview.py +++ b/cloudview/cloudview.py @@ -52,9 +52,7 @@ def get_clients( providers = ( (provider,) if provider - else config["providers"].keys() - if config - else PROVIDERS.keys() + else config["providers"].keys() if config else PROVIDERS.keys() ) clients = [] for xprovider in providers: @@ -66,9 +64,7 @@ def get_clients( clouds = ( (cloud,) if cloud - else config["providers"][xprovider].keys() - if config - else ("",) + else config["providers"][xprovider].keys() if config else ("",) ) for xcloud in clouds: try: @@ -100,6 +96,7 @@ def print_instances(client: CSP) -> None: params = urlencode(instance.params) resource = "/".join([instance.provider.lower(), f"{instance.id}?{params}"]) instance.href = f"instance/{resource}" + assert not isinstance(instance.time, str) instance.time = dateit(instance.time, args.time) Output().info(instance) diff --git a/cloudview/ec2.py b/cloudview/ec2.py index fa9a10f..dc9ba9d 100644 --- a/cloudview/ec2.py +++ b/cloudview/ec2.py @@ -81,7 +81,7 @@ def _node_to_instance(self, node: Node, region: str) -> Instance: provider=Provider.EC2, cloud=self.cloud, name=node.extra["tags"].get("Name", node.name), - id=node.id, + id=str(node.id), size=node.extra["instance_type"], time=utc_date(node.extra["launch_time"]), state=node.state, diff --git a/cloudview/gce.py b/cloudview/gce.py index 6092152..bc4ebc1 100644 --- a/cloudview/gce.py +++ b/cloudview/gce.py @@ -104,7 +104,7 @@ def _node_to_instance(self, node: Node) -> Instance: provider=Provider.GCE, cloud=self.cloud, name=node.name, - id=node.id, + id=str(node.id), size=node.extra["machineType"].split("/")[-1], time=utc_date(node.extra["creationTimestamp"]), state=node.state, diff --git a/cloudview/instance.py b/cloudview/instance.py index 90741d1..9cf122d 100644 --- a/cloudview/instance.py +++ b/cloudview/instance.py @@ -3,6 +3,8 @@ """ import logging +from dataclasses import dataclass +from datetime import datetime from typing import Any from cachetools import cached, TTLCache @@ -15,18 +17,22 @@ STATES = [str(getattr(NodeState, _)) for _ in dir(NodeState) if _.isupper()] -class Instance: +@dataclass(kw_only=True) +class Instance: # pylint: disable=too-many-instance-attributes """ Instance class """ - def __init__(self, **kwargs): - for attr, value in kwargs.items(): - setattr(self, attr, value) - - def __repr__(self) -> str: - attrs = ", ".join(f"{attr}={getattr(self, attr)!r}" for attr in vars(self)) - return f"{self.__class__.__name__}({attrs})" + provider: str + cloud: str + name: str + id: str + size: str + time: str | datetime + state: str + location: str + extra: dict + params: dict # Allow access this object as a dictionary diff --git a/cloudview/openstack.py b/cloudview/openstack.py index 974f8be..e306581 100644 --- a/cloudview/openstack.py +++ b/cloudview/openstack.py @@ -120,7 +120,7 @@ def _node_to_instance(self, node: Node) -> Instance: provider=Provider.OPENSTACK, cloud=self.cloud, name=node.name, - id=node.id, + id=str(node.id), size=self._get_size(node.extra["flavorId"]), time=utc_date(node.extra["created"]), state=node.state, diff --git a/tests/test_ec2.py b/tests/test_ec2.py index d85cad2..e87b21a 100644 --- a/tests/test_ec2.py +++ b/tests/test_ec2.py @@ -50,6 +50,11 @@ def mock_ec2_instance(): "availability": "us-east-1a", "tags": {"Name": "test-instance"}, }, + provider="Provider", + cloud="Cloud", + time="2023-04-19T13:04:22.000Z", + location="Location", + params={}, ) diff --git a/tests/test_instance.py b/tests/test_instance.py index 5635604..e2ab4c7 100644 --- a/tests/test_instance.py +++ b/tests/test_instance.py @@ -5,39 +5,90 @@ def test_instance_repr(): - item = Instance(name="Example", value=42, none=None, map={1: "2"}) + item = Instance( + name="Example", + provider="P", + cloud="C", + id="id", + size="s", + time="T", + state="Running", + location="L", + extra={}, + params={}, + ) repr_string = repr(item) recreated_item = eval(repr_string) assert item.__dict__ == recreated_item.__dict__ def test_instance_creation(): - instance = Instance(name="Example", type="VM", status="Running") + instance = Instance( + name="Example", + provider="P", + cloud="C", + id="id", + size="s", + time="T", + state="Running", + location="L", + extra={}, + params={}, + ) assert instance.name == "Example" - assert instance.type == "VM" - assert instance.status == "Running" + assert instance.state == "Running" def test_instance_dict_access(): - instance = Instance(name="Example", type="VM", status="Running") + instance = Instance( + name="Example", + provider="P", + cloud="C", + id="id", + size="s", + time="T", + state="Running", + location="L", + extra={}, + params={}, + ) assert instance["name"] == "Example" - assert instance["type"] == "VM" - assert instance["status"] == "Running" + assert instance["state"] == "Running" def test_instance_dict_assignment(): - instance = Instance() + instance = Instance( + name="name", + provider="P", + cloud="C", + id="id", + size="s", + time="T", + state="S", + location="L", + extra={}, + params={}, + ) instance["name"] = "Updated Name" - instance["type"] = "Updated Type" assert instance.name == "Updated Name" - assert instance.type == "Updated Type" def test_instance_dict_deletion(): - instance = Instance(name="Alice", age=30) + instance = Instance( + name="name", + provider="P", + cloud="C", + id="id", + size="s", + time="T", + state="S", + location="L", + extra={}, + params={}, + ) with pytest.raises(KeyError): del instance["nonexistent"] @@ -50,7 +101,18 @@ def test_instance_dict_deletion(): def test_instance_unknown_attribute(): - instance = Instance() + instance = Instance( + name="name", + provider="P", + cloud="C", + id="id", + size="s", + time="T", + state="S", + location="L", + extra={}, + params={}, + ) with pytest.raises(AttributeError): _ = instance.unknown_attribute @@ -61,15 +123,27 @@ def _get_instances(self): return [ Instance( id="id1", - instance_id="id1", name="Instance1", extra={"status": "running"}, + provider="P", + cloud="C", + size="s", + time="T", + state="S", + location="L", + params={}, ), Instance( id="id2", - instance_id="id2", name="Instance2", extra={"status": "stopped"}, + provider="P", + cloud="C", + size="s", + time="T", + state="S", + location="L", + params={}, ), ] @@ -77,9 +151,15 @@ def _get_instance(self, instance_id, params): if instance_id == "id1": return Instance( id="id1", - instance_id="id1", name="Instance1", extra={"name=": "Instance1"}, + provider="P", + cloud="C", + size="s", + time="T", + state="S", + location="L", + params={}, ) return None @@ -94,7 +174,7 @@ def test_csp_get_instances(): instances = csp.get_instances() assert len(instances) == 2 assert instances[0].name == "Instance1" - assert instances[1].instance_id == "id2" + assert instances[1].id == "id2" def test_csp_get_instance(): diff --git a/tests/test_output.py b/tests/test_output.py index b0a3f24..d31a61a 100644 --- a/tests/test_output.py +++ b/tests/test_output.py @@ -82,7 +82,18 @@ def test_json_output_dict(json_output, capsys): def test_json_output_obj(json_output, capsys): json_output.header() - info = Instance(name="instance-1", age="30") + info = Instance( + name="instance-1", + provider="P", + cloud="C", + id="id", + size="s", + time="T", + state="S", + location="L", + extra={}, + params={}, + ) json_output.info(info) json_output.footer() captured = capsys.readouterr()