Coverage for nova/virt/node.py: 97%
54 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 2022 Red Hat, inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
15import logging
16import os
17import uuid
19from oslo_utils import uuidutils
21import nova.conf
22from nova import exception
24CONF = nova.conf.CONF
25LOG = logging.getLogger(__name__)
26COMPUTE_ID_FILE = 'compute_id'
27LOCAL_NODE_UUID = None
30def write_local_node_uuid(node_uuid):
31 # We only ever write an identity file in the CONF.state_path
32 # location
33 fn = os.path.join(CONF.state_path, COMPUTE_ID_FILE)
35 # Try to create the identity file and write our uuid into it. Fail
36 # if the file exists (since it shouldn't if we made it here).
37 try:
38 with open(fn, 'x') as f:
39 f.write(node_uuid)
40 except FileExistsError:
41 # If the file exists, we must either fail or re-survey all the
42 # potential files. If we just read and return it, it could be
43 # inconsistent with files in the other locations.
44 raise exception.InvalidNodeConfiguration(
45 reason='Identity file %s appeared unexpectedly' % fn)
46 except Exception as e:
47 raise exception.InvalidNodeConfiguration(
48 reason='Unable to write uuid to %s: %s' % (fn, e))
50 LOG.info('Wrote node identity %s to %s', node_uuid, fn)
53def read_local_node_uuid():
54 locations = ([os.path.dirname(f) for f in CONF.config_file] +
55 [CONF.state_path])
57 uuids = []
58 found = []
59 for location in locations:
60 fn = os.path.join(location, COMPUTE_ID_FILE)
61 try:
62 # UUIDs should be 36 characters in canonical format. Read
63 # a little more to be graceful about whitespace in/around
64 # the actual value we want to read. However, it must parse
65 # to a legit UUID once we strip the whitespace.
66 with open(fn) as f:
67 content = f.read(40)
68 node_uuid = str(uuid.UUID(content.strip()))
69 except FileNotFoundError:
70 continue
71 except ValueError:
72 raise exception.InvalidNodeConfiguration(
73 reason='Unable to parse UUID from %s' % fn)
74 uuids.append(node_uuid)
75 found.append(fn)
77 if uuids:
78 # Any identities we found must be consistent, or we fail
79 first = uuids[0]
80 for i, (node_uuid, fn) in enumerate(zip(uuids, found)):
81 if node_uuid != first:
82 raise exception.InvalidNodeConfiguration(
83 reason='UUID %s in %s does not match %s' % (
84 node_uuid, fn, uuids[i - 1]))
85 LOG.info('Determined node identity %s from %s', first, found[0])
86 return first
87 else:
88 return None
91def get_local_node_uuid():
92 """Read or create local node uuid file.
94 :returns: UUID string read from file, or generated
95 """
96 global LOCAL_NODE_UUID
98 if LOCAL_NODE_UUID is not None:
99 return LOCAL_NODE_UUID
101 node_uuid = read_local_node_uuid()
102 if not node_uuid:
103 node_uuid = uuidutils.generate_uuid()
104 LOG.info('Generated node identity %s', node_uuid)
105 write_local_node_uuid(node_uuid)
107 LOCAL_NODE_UUID = node_uuid
108 return node_uuid