Coverage for nova/virt/node.py: 97%

54 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-04-24 11:16 +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. 

14 

15import logging 

16import os 

17import uuid 

18 

19from oslo_utils import uuidutils 

20 

21import nova.conf 

22from nova import exception 

23 

24CONF = nova.conf.CONF 

25LOG = logging.getLogger(__name__) 

26COMPUTE_ID_FILE = 'compute_id' 

27LOCAL_NODE_UUID = None 

28 

29 

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) 

34 

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)) 

49 

50 LOG.info('Wrote node identity %s to %s', node_uuid, fn) 

51 

52 

53def read_local_node_uuid(): 

54 locations = ([os.path.dirname(f) for f in CONF.config_file] + 

55 [CONF.state_path]) 

56 

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) 

76 

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 

89 

90 

91def get_local_node_uuid(): 

92 """Read or create local node uuid file. 

93 

94 :returns: UUID string read from file, or generated 

95 """ 

96 global LOCAL_NODE_UUID 

97 

98 if LOCAL_NODE_UUID is not None: 

99 return LOCAL_NODE_UUID 

100 

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) 

106 

107 LOCAL_NODE_UUID = node_uuid 

108 return node_uuid