Coverage for nova/compute/stats.py: 93%
82 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-17 15:08 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-17 15:08 +0000
1# Copyright (c) 2012 OpenStack Foundation
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
16from nova.compute import task_states
17from nova.compute import vm_states
18from nova.i18n import _
21class Stats(dict):
22 """Handler for updates to compute node workload stats."""
24 def __init__(self):
25 super(Stats, self).__init__()
27 # Track instance states for compute node workload calculations:
28 self.states = {}
30 def clear(self):
31 super(Stats, self).clear()
33 self.states.clear()
35 def digest_stats(self, stats):
36 """Apply stats provided as a dict or a json encoded string."""
37 if stats is None: 37 ↛ 39line 37 didn't jump to line 39 because the condition on line 37 was always true
38 return
39 if isinstance(stats, dict):
40 self.update(stats)
41 return
42 raise ValueError(_('Unexpected type adding stats'))
44 @property
45 def io_workload(self):
46 """Calculate an I/O based load by counting I/O heavy operations."""
48 def _get(state, state_type):
49 key = "num_%s_%s" % (state_type, state)
50 return self.get(key, 0)
52 num_builds = _get(vm_states.BUILDING, "vm")
53 num_migrations = _get(task_states.RESIZE_MIGRATING, "task")
54 num_rebuilds = _get(task_states.REBUILDING, "task")
55 num_resizes = _get(task_states.RESIZE_PREP, "task")
56 num_snapshots = _get(task_states.IMAGE_SNAPSHOT, "task")
57 num_backups = _get(task_states.IMAGE_BACKUP, "task")
58 num_rescues = _get(task_states.RESCUING, "task")
59 num_unshelves = _get(task_states.UNSHELVING, "task")
61 return (num_builds + num_rebuilds + num_resizes + num_migrations +
62 num_snapshots + num_backups + num_rescues + num_unshelves)
64 def calculate_workload(self):
65 """Calculate current load of the compute host based on
66 task states.
67 """
68 current_workload = 0
69 for k in self:
70 if k.startswith("num_task") and not k.endswith("None"):
71 current_workload += self[k]
72 return current_workload
74 @property
75 def num_instances(self):
76 return self.get("num_instances", 0)
78 def num_instances_for_project(self, project_id):
79 key = "num_proj_%s" % project_id
80 return self.get(key, 0)
82 def num_os_type(self, os_type):
83 key = "num_os_type_%s" % os_type
84 return self.get(key, 0)
86 def update_stats_for_instance(self, instance, is_removed=False):
87 """Update stats after an instance is changed."""
89 uuid = instance['uuid']
91 # First, remove stats from the previous instance
92 # state:
93 if uuid in self.states:
94 old_state = self.states[uuid]
96 self._decrement("num_vm_%s" % old_state['vm_state'])
97 self._decrement("num_task_%s" % old_state['task_state'])
98 self._decrement("num_os_type_%s" % old_state['os_type'])
99 self._decrement("num_proj_%s" % old_state['project_id'])
100 else:
101 # new instance
102 self._increment("num_instances")
104 # Now update stats from the new instance state:
105 (vm_state, task_state, os_type, project_id) = \
106 self._extract_state_from_instance(instance)
108 if is_removed or vm_states.allow_resource_removal(
109 vm_state=vm_state, task_state=task_state):
110 self._decrement("num_instances")
111 self.states.pop(uuid)
112 else:
113 self._increment("num_vm_%s" % vm_state)
114 self._increment("num_task_%s" % task_state)
115 self._increment("num_os_type_%s" % os_type)
116 self._increment("num_proj_%s" % project_id)
118 # save updated I/O workload in stats:
119 self["io_workload"] = self.io_workload
121 def _decrement(self, key):
122 x = self.get(key, 0)
123 self[key] = x - 1
125 def _increment(self, key):
126 x = self.get(key, 0)
127 self[key] = x + 1
129 def _extract_state_from_instance(self, instance):
130 """Save the useful bits of instance state for tracking purposes."""
132 uuid = instance['uuid']
133 vm_state = instance['vm_state']
134 task_state = instance['task_state']
135 os_type = instance['os_type']
136 project_id = instance['project_id']
138 self.states[uuid] = dict(vm_state=vm_state, task_state=task_state,
139 os_type=os_type, project_id=project_id)
141 return (vm_state, task_state, os_type, project_id)
143 def build_failed(self):
144 self['failed_builds'] = self.get('failed_builds', 0) + 1
146 def build_succeeded(self):
147 # FIXME(danms): Make this more graceful, either by time-based aging or
148 # a fixed decline upon success
149 self['failed_builds'] = 0