1# Copyright (C) 2018 Red Hat, Inc.
2#
3# This work is licensed under the GNU GPLv2 or later.
4# See the COPYING file in the top-level directory.
5
6import re
7
8import libvirt
9
10from virtinst import log
11
12
13class _LibvirtEnumMap(object):
14    """
15    Helper for mapping libvirt event int values to their API names
16    """
17    # Some values we define to distinguish between API objects
18    (DOMAIN_EVENT,
19     DOMAIN_AGENT_EVENT,
20     NETWORK_EVENT,
21     STORAGE_EVENT,
22     NODEDEV_EVENT) = range(1, 6)
23
24    # Regex map for naming all event types depending on the API object
25    _EVENT_PREFIX = {
26        DOMAIN_EVENT: "VIR_DOMAIN_EVENT_ID_",
27        DOMAIN_AGENT_EVENT: "VIR_DOMAIN_EVENT_ID_AGENT_",
28        NETWORK_EVENT: "VIR_NETWORK_EVENT_ID_",
29        STORAGE_EVENT: "VIR_STORAGE_POOL_EVENT_ID_",
30        NODEDEV_EVENT: "VIR_NODE_DEVICE_EVENT_ID_",
31    }
32
33    # Regex map for 'state' values returned from lifecycle and other events
34    _DETAIL1_PREFIX = {
35        "VIR_DOMAIN_EVENT_ID_LIFECYCLE": "VIR_DOMAIN_EVENT_[^_]+$",
36        "VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE": "VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_[^_]+$",
37        "VIR_NETWORK_EVENT_ID_LIFECYCLE": "VIR_NETWORK_EVENT_[^_]+$",
38        "VIR_STORAGE_POOL_EVENT_ID_LIFECYCLE": "VIR_STORAGE_POOL_EVENT_[^_]+$",
39        "VIR_NODE_DEVICE_EVENT_ID_LIFECYCLE": "VIR_NODE_DEVICE_EVENT_[^_]+$",
40    }
41
42    # Regex map for 'reason' values returned from lifecycle and other events
43    _DETAIL2_PREFIX = {
44        "VIR_DOMAIN_EVENT_DEFINED": "VIR_DOMAIN_EVENT_DEFINED_",
45        "VIR_DOMAIN_EVENT_UNDEFINED": "VIR_DOMAIN_EVENT_UNDEFINED_",
46        "VIR_DOMAIN_EVENT_STARTED": "VIR_DOMAIN_EVENT_STARTED_",
47        "VIR_DOMAIN_EVENT_SUSPENDED": "VIR_DOMAIN_EVENT_SUSPENDED_",
48        "VIR_DOMAIN_EVENT_RESUMED": "VIR_DOMAIN_EVENT_RESUMED_",
49        "VIR_DOMAIN_EVENT_STOPPED": "VIR_DOMAIN_EVENT_STOPPED_",
50        "VIR_DOMAIN_EVENT_SHUTDOWN": "VIR_DOMAIN_EVENT_SHUTDOWN_",
51        "VIR_DOMAIN_EVENT_PMSUSPENDED": "VIR_DOMAIN_EVENT_PMSUSPENDED_",
52        "VIR_DOMAIN_EVENT_CRASHED": "VIR_DOMAIN_EVENT_CRASHED_",
53    }
54
55    VM_STATUS_ICONS = {
56        libvirt.VIR_DOMAIN_BLOCKED: "state_running",
57        libvirt.VIR_DOMAIN_CRASHED: "state_shutoff",
58        libvirt.VIR_DOMAIN_PAUSED: "state_paused",
59        libvirt.VIR_DOMAIN_RUNNING: "state_running",
60        libvirt.VIR_DOMAIN_SHUTDOWN: "state_shutoff",
61        libvirt.VIR_DOMAIN_SHUTOFF: "state_shutoff",
62        libvirt.VIR_DOMAIN_NOSTATE: "state_running",
63        libvirt.VIR_DOMAIN_PMSUSPENDED: "state_paused",
64    }
65
66    @staticmethod
67    def pretty_run_status(status, has_managed_save):
68        if status == libvirt.VIR_DOMAIN_RUNNING:
69            return _("Running")
70        elif status == libvirt.VIR_DOMAIN_PAUSED:
71            return _("Paused")
72        elif status == libvirt.VIR_DOMAIN_SHUTDOWN:
73            return _("Shutting Down")  # pragma: no cover
74        elif status == libvirt.VIR_DOMAIN_SHUTOFF:
75            if has_managed_save:
76                return _("Saved")
77            else:
78                return _("Shutoff")
79        elif status == libvirt.VIR_DOMAIN_CRASHED:
80            return _("Crashed")
81        elif status == libvirt.VIR_DOMAIN_PMSUSPENDED:
82            return _("Suspended")
83
84        log.debug(  # pragma: no cover
85                "Unknown status %s, returning 'Unknown'", status)
86        return _("Unknown")  # pragma: no cover
87
88    @staticmethod
89    def pretty_status_reason(status, reason):
90        def key(x, y):
91            return getattr(libvirt, "VIR_DOMAIN_" + x, y)
92        reasons = {
93            libvirt.VIR_DOMAIN_RUNNING: {
94                key("RUNNING_BOOTED", 1):             _("Booted"),
95                key("RUNNING_MIGRATED", 2):           _("Migrated"),
96                key("RUNNING_RESTORED", 3):           _("Restored"),
97                key("RUNNING_FROM_SNAPSHOT", 4):      _("From snapshot"),
98                key("RUNNING_UNPAUSED", 5):           _("Unpaused"),
99                key("RUNNING_MIGRATION_CANCELED", 6): _("Migration canceled"),
100                key("RUNNING_SAVE_CANCELED", 7):      _("Save canceled"),
101                key("RUNNING_WAKEUP", 8):             _("Event wakeup"),
102                key("RUNNING_CRASHED", 9):            _("Crashed"),
103            },
104            libvirt.VIR_DOMAIN_PAUSED: {
105                key("PAUSED_USER", 1):                _("User"),
106                key("PAUSED_MIGRATION", 2):           _("Migrating"),
107                key("PAUSED_SAVE", 3):                _("Saving"),
108                key("PAUSED_DUMP", 4):                _("Dumping"),
109                key("PAUSED_IOERROR", 5):             _("I/O error"),
110                key("PAUSED_WATCHDOG", 6):            _("Watchdog"),
111                key("PAUSED_FROM_SNAPSHOT", 7):       _("From snapshot"),
112                key("PAUSED_SHUTTING_DOWN", 8):       _("Shutting down"),
113                key("PAUSED_SNAPSHOT", 9):            _("Creating snapshot"),
114                key("PAUSED_CRASHED", 10):            _("Crashed"),
115            },
116            libvirt.VIR_DOMAIN_SHUTDOWN: {
117                key("SHUTDOWN_USER", 1):              _("User"),
118            },
119            libvirt.VIR_DOMAIN_SHUTOFF: {
120                key("SHUTOFF_SHUTDOWN", 1):           _("Shut Down"),
121                key("SHUTOFF_DESTROYED", 2):          _("Destroyed"),
122                key("SHUTOFF_CRASHED", 3):            _("Crashed"),
123                key("SHUTOFF_MIGRATED", 4):           _("Migrated"),
124                key("SHUTOFF_SAVED", 5):              _("Saved"),
125                key("SHUTOFF_FAILED", 6):             _("Failed"),
126                key("SHUTOFF_FROM_SNAPSHOT", 7):      _("From snapshot"),
127            },
128            libvirt.VIR_DOMAIN_CRASHED: {
129                key("CRASHED_PANICKED", 1):           _("Panicked"),
130            }
131        }
132        return reasons.get(status) and reasons[status].get(reason)
133
134    def __init__(self):
135        self._mapping = {}
136
137    def _make_map(self, regex):
138        # Run the passed regex over dir(libvirt) output
139        ret = {}
140        for key in [a for a in dir(libvirt) if re.match(regex, a)]:
141            val = getattr(libvirt, key)
142            if type(val) is not int:  # pragma: no cover
143                log.debug("libvirt regex=%s key=%s val=%s "
144                    "isn't an integer", regex, key, val)
145                continue
146            if val in ret:  # pragma: no cover
147                log.debug("libvirt regex=%s key=%s val=%s is already "
148                    "in dict as key=%s", regex, key, val, regex[val])
149                continue
150            ret[val] = key
151        return ret
152
153    def _get_map(self, key, regex):
154        if regex is None:
155            return {}
156        if key not in self._mapping:
157            self._mapping[key] = self._make_map(regex)
158        return self._mapping[key]
159
160    def _make_strs(self, api, event, detail1, detail2):
161        eventstr = str(event)
162        detail1str = str(detail1)
163        detail2str = str(detail2)
164        eventmap = self._get_map(api, self._EVENT_PREFIX[api])
165
166        if eventmap:
167            if event not in eventmap:
168                event = next(iter(eventmap))  # pragma: no cover
169            eventstr = eventmap[event]
170            detail1map = self._get_map(eventstr,
171                    self._DETAIL1_PREFIX.get(eventstr))
172            if detail1 in detail1map:
173                detail1str = detail1map[detail1]
174                detail2map = self._get_map(detail1str,
175                        self._DETAIL2_PREFIX.get(detail1str))
176                if detail2 in detail2map:
177                    detail2str = detail2map[detail2]
178
179        return eventstr, detail1str, detail2str
180
181    def _state_str(self, api, detail1, detail2):
182        ignore, d1str, d2str = self._make_strs(api, 0,
183                detail1, detail2)
184        return "state=%s reason=%s" % (d1str, d2str)
185
186    def domain_lifecycle_str(self, detail1, detail2):
187        return self._state_str(self.DOMAIN_EVENT, detail1, detail2)
188    def network_lifecycle_str(self, detail1, detail2):
189        return self._state_str(self.NETWORK_EVENT, detail1, detail2)
190    def storage_lifecycle_str(self, detail1, detail2):
191        return self._state_str(self.STORAGE_EVENT, detail1, detail2)
192    def nodedev_lifecycle_str(self, detail1, detail2):
193        return self._state_str(self.NODEDEV_EVENT, detail1, detail2)
194    def domain_agent_lifecycle_str(self, detail1, detail2):
195        return self._state_str(self.DOMAIN_AGENT_EVENT, detail1, detail2)
196
197
198LibvirtEnumMap = _LibvirtEnumMap()
199