diff options
Diffstat (limited to 'nova/virt/node.py')
-rw-r--r-- | nova/virt/node.py | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/nova/virt/node.py b/nova/virt/node.py new file mode 100644 index 0000000000..4cb3d0a573 --- /dev/null +++ b/nova/virt/node.py @@ -0,0 +1,108 @@ +# Copyright 2022 Red Hat, inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging +import os +import uuid + +from oslo_utils import uuidutils + +import nova.conf +from nova import exception + +CONF = nova.conf.CONF +LOG = logging.getLogger(__name__) +COMPUTE_ID_FILE = 'compute_id' +LOCAL_NODE_UUID = None + + +def write_local_node_uuid(node_uuid): + # We only ever write an identity file in the CONF.state_path + # location + fn = os.path.join(CONF.state_path, COMPUTE_ID_FILE) + + # Try to create the identity file and write our uuid into it. Fail + # if the file exists (since it shouldn't if we made it here). + try: + with open(fn, 'x') as f: + f.write(node_uuid) + except FileExistsError: + # If the file exists, we must either fail or re-survey all the + # potential files. If we just read and return it, it could be + # inconsistent with files in the other locations. + raise exception.InvalidNodeConfiguration( + reason='Identity file %s appeared unexpectedly' % fn) + except Exception as e: + raise exception.InvalidNodeConfiguration( + reason='Unable to write uuid to %s: %s' % (fn, e)) + + LOG.info('Wrote node identity %s to %s', node_uuid, fn) + + +def read_local_node_uuid(): + locations = ([os.path.dirname(f) for f in CONF.config_file] + + [CONF.state_path]) + + uuids = [] + found = [] + for location in locations: + fn = os.path.join(location, COMPUTE_ID_FILE) + try: + # UUIDs should be 36 characters in canonical format. Read + # a little more to be graceful about whitespace in/around + # the actual value we want to read. However, it must parse + # to a legit UUID once we strip the whitespace. + with open(fn) as f: + content = f.read(40) + node_uuid = str(uuid.UUID(content.strip())) + except FileNotFoundError: + continue + except ValueError: + raise exception.InvalidNodeConfiguration( + reason='Unable to parse UUID from %s' % fn) + uuids.append(node_uuid) + found.append(fn) + + if uuids: + # Any identities we found must be consistent, or we fail + first = uuids[0] + for i, (node_uuid, fn) in enumerate(zip(uuids, found)): + if node_uuid != first: + raise exception.InvalidNodeConfiguration( + reason='UUID %s in %s does not match %s' % ( + node_uuid, fn, uuids[i - 1])) + LOG.info('Determined node identity %s from %s', first, found[0]) + return first + else: + return None + + +def get_local_node_uuid(): + """Read or create local node uuid file. + + :returns: UUID string read from file, or generated + """ + global LOCAL_NODE_UUID + + if LOCAL_NODE_UUID is not None: + return LOCAL_NODE_UUID + + node_uuid = read_local_node_uuid() + if not node_uuid: + node_uuid = uuidutils.generate_uuid() + LOG.info('Generated node identity %s', node_uuid) + write_local_node_uuid(node_uuid) + + LOCAL_NODE_UUID = node_uuid + return node_uuid |