Test Failed
Push — master ( cc9054...a8608f )
by Nicolas
03:40
created

VmExtension.generate_stats()   C

Complexity

Conditions 10

Size

Total Lines 15
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 14
nop 3
dl 0
loc 15
rs 5.9999
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like glances.plugins.vms.engines.multipass.VmExtension.generate_stats() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#
2
# This file is part of Glances.
3
#
4
# SPDX-FileCopyrightText: 2024 Nicolas Hennion <[email protected]>
5
#
6
# SPDX-License-Identifier: LGPL-3.0-only
7
#
8
9
"""Multipass Extension unit for Glances' Vms plugin."""
10
11
import json
12
import os
13
from typing import Any, Dict, List, Tuple
14
15
from glances.globals import json_loads, nativestr
16
from glances.secure import secure_popen
17
18
# Check if multipass binary exist
19
# TODO: make this path configurable from the Glances configuration file
20
MULTIPASS_PATH = '/snap/bin/multipass'
21
MULTIPASS_VERSION_OPTIONS = 'version --format json'
22
MULTIPASS_INFO_OPTIONS = 'info --format json'
23
import_multipass_error_tag = not os.path.exists(MULTIPASS_PATH)
24
25
26
class VmExtension:
27
    """Glances' Vms Plugin's Vm Extension unit"""
28
29
    CONTAINER_ACTIVE_STATUS = ['running']
30
31
    def __init__(self):
32
        self.ext_name = "Multipass (Vm)"
33
34
    def update_version(self):
35
        # > multipass version --format json
36
        # {
37
        #     "multipass": "1.13.1",
38
        #     "multipassd": "1.13.1"
39
        # }
40
        ret_cmd = secure_popen(f'{MULTIPASS_PATH} {MULTIPASS_VERSION_OPTIONS}')
41
        try:
42
            ret = json_loads(ret_cmd)
43
        except json.JSONDecodeError:
44
            return {}
45
        else:
46
            return ret.get('multipass', None)
47
48
    def update_info(self):
49
        # > multipass info  --format json
50
        # {
51
        #     "errors": [
52
        #     ],
53
        #     "info": {
54
        #         "adapted-budgerigar": {
55
        #             "cpu_count": "1",
56
        #             "disks": {
57
        #                 "sda1": {
58
        #                     "total": "5116440064",
59
        #                     "used": "2287162880"
60
        #                 }
61
        #             },
62
        #             "image_hash": "182dc760bfca26c45fb4e4668049ecd4d0ecdd6171b3bae81d0135e8f1e9d93e",
63
        #             "image_release": "24.04 LTS",
64
        #             "ipv4": [
65
        #                 "10.160.166.174"
66
        #             ],
67
        #             "load": [
68
        #                 0,
69
        #                 0.03,
70
        #                 0
71
        #             ],
72
        #             "memory": {
73
        #                 "total": 1002500096,
74
        #                 "used": 432058368
75
        #             },
76
        #             "mounts": {
77
        #             },
78
        #             "release": "Ubuntu 24.04 LTS",
79
        #             "snapshot_count": "0",
80
        #             "state": "Running"
81
        #         }
82
        #     }
83
        # }
84
        ret_cmd = secure_popen(f'{MULTIPASS_PATH} {MULTIPASS_INFO_OPTIONS}')
85
        try:
86
            ret = json_loads(ret_cmd)
87
        except json.JSONDecodeError:
88
            return {}
89
        else:
90
            return ret.get('info', {})
91
92
    def update(self, all_tag) -> Tuple[Dict, List[Dict]]:
93
        """Update Vm stats using the input method."""
94
        # Can not run multipass on this system then...
95
        if import_multipass_error_tag:
96
            return {}, []
97
98
        # Get the stats from the system
99
        version_stats = self.update_version()
100
        info_stats = self.update_info()
101
        returned_stats = []
102
        for k, v in info_stats.items():
103
            # Only display when VM in on 'running' states
104
            # See states list here: https://multipass.run/docs/instance-states
105
            if all_tag or self._want_display(v, 'state', ['Running', 'Starting', 'Restarting']):
106
                returned_stats.append(self.generate_stats(k, v))
107
108
        return version_stats, returned_stats
109
110
    @property
111
    def key(self) -> str:
112
        """Return the key of the list."""
113
        return 'name'
114
115
    def _want_display(self, vm_stats, key, values):
116
        return vm_stats.get(key).lower() in [v.lower() for v in values]
117
118
    def generate_stats(self, vm_name, vm_stats) -> Dict[str, Any]:
119
        # Init the stats for the current vm
120
        return {
121
            'key': self.key,
122
            'name': nativestr(vm_name),
123
            'id': vm_stats.get('image_hash'),
124
            'status': vm_stats.get('state').lower() if vm_stats.get('state') else None,
125
            'release': vm_stats.get('release') if len(vm_stats.get('release')) > 0 else vm_stats.get('image_release'),
126
            'cpu_count': int(vm_stats.get('cpu_count', 1)) if len(vm_stats.get('cpu_count', 1)) > 0 else None,
127
            'memory_usage': vm_stats.get('memory').get('used') if vm_stats.get('memory') else None,
128
            'memory_total': vm_stats.get('memory').get('total') if vm_stats.get('memory') else None,
129
            'load_1min': vm_stats.get('load')[0] if vm_stats.get('load') else None,
130
            'load_5min': vm_stats.get('load')[1] if vm_stats.get('load') else None,
131
            'load_15min': vm_stats.get('load')[2] if vm_stats.get('load') else None,
132
            'ipv4': vm_stats.get('ipv4')[0] if len(vm_stats.get('ipv4')) > 0 else None,
133
            # TODO: disk
134
        }
135