Coverage for nova/api/openstack/compute/views/images.py: 98%

50 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-04-24 11:16 +0000

1# Copyright 2010-2011 OpenStack Foundation 

2# Copyright 2013 IBM Corp. 

3# All Rights Reserved. 

4# 

5# Licensed under the Apache License, Version 2.0 (the "License"); you may 

6# not use this file except in compliance with the License. You may obtain 

7# a copy of the License at 

8# 

9# http://www.apache.org/licenses/LICENSE-2.0 

10# 

11# Unless required by applicable law or agreed to in writing, software 

12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 

13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 

14# License for the specific language governing permissions and limitations 

15# under the License. 

16 

17from oslo_utils import strutils 

18 

19from nova.api.openstack import common 

20from nova.image import glance 

21from nova import utils 

22 

23 

24class ViewBuilder(common.ViewBuilder): 

25 

26 _collection_name = "images" 

27 

28 def basic(self, request, image): 

29 """Return a dictionary with basic image attributes.""" 

30 return { 

31 "image": { 

32 "id": image.get("id"), 

33 "name": image.get("name"), 

34 "links": self._get_links(request, 

35 image["id"], 

36 self._collection_name), 

37 }, 

38 } 

39 

40 def show(self, request, image): 

41 """Return a dictionary with image details.""" 

42 image_dict = { 

43 "id": image.get("id"), 

44 "name": image.get("name"), 

45 "minRam": int(image.get("min_ram") or 0), 

46 "minDisk": int(image.get("min_disk") or 0), 

47 "metadata": image.get("properties", {}), 

48 "created": self._format_date(image.get("created_at")), 

49 "updated": self._format_date(image.get("updated_at")), 

50 "status": self._get_status(image), 

51 "progress": self._get_progress(image), 

52 "OS-EXT-IMG-SIZE:size": image.get("size"), 

53 "links": self._get_links(request, 

54 image["id"], 

55 self._collection_name), 

56 } 

57 

58 instance_uuid = image.get("properties", {}).get("instance_uuid") 

59 

60 if instance_uuid is not None: 

61 server_ref = self._get_href_link(request, instance_uuid, 'servers') 

62 image_dict["server"] = { 

63 "id": instance_uuid, 

64 "links": [{ 

65 "rel": "self", 

66 "href": server_ref, 

67 }, 

68 { 

69 "rel": "bookmark", 

70 "href": self._get_bookmark_link(request, 

71 instance_uuid, 

72 'servers'), 

73 }], 

74 } 

75 

76 auto_disk_config = image_dict['metadata'].get("auto_disk_config", None) 

77 if auto_disk_config is not None: 

78 value = strutils.bool_from_string(auto_disk_config) 

79 image_dict["OS-DCF:diskConfig"] = ( 

80 'AUTO' if value else 'MANUAL') 

81 

82 return dict(image=image_dict) 

83 

84 def detail(self, request, images): 

85 """Show a list of images with details.""" 

86 list_func = self.show 

87 coll_name = self._collection_name + '/detail' 

88 return self._list_view(list_func, request, images, coll_name) 

89 

90 def index(self, request, images): 

91 """Show a list of images with basic attributes.""" 

92 list_func = self.basic 

93 coll_name = self._collection_name 

94 return self._list_view(list_func, request, images, coll_name) 

95 

96 def _list_view(self, list_func, request, images, coll_name): 

97 """Provide a view for a list of images. 

98 

99 :param list_func: Function used to format the image data 

100 :param request: API request 

101 :param images: List of images in dictionary format 

102 :param coll_name: Name of collection, used to generate the next link 

103 for a pagination query 

104 

105 :returns: Image reply data in dictionary format 

106 """ 

107 image_list = [list_func(request, image)["image"] for image in images] 

108 images_links = self._get_collection_links(request, images, coll_name) 

109 images_dict = dict(images=image_list) 

110 

111 if images_links: 

112 images_dict["images_links"] = images_links 

113 

114 return images_dict 

115 

116 def _get_links(self, request, identifier, collection_name): 

117 """Return a list of links for this image.""" 

118 return [{ 

119 "rel": "self", 

120 "href": self._get_href_link(request, identifier, collection_name), 

121 }, 

122 { 

123 "rel": "bookmark", 

124 "href": self._get_bookmark_link(request, 

125 identifier, 

126 collection_name), 

127 }, 

128 { 

129 "rel": "alternate", 

130 "type": "application/vnd.openstack.image", 

131 "href": self._get_alternate_link(request, identifier), 

132 }] 

133 

134 def _get_alternate_link(self, request, identifier): 

135 """Create an alternate link for a specific image id.""" 

136 glance_url = glance.generate_glance_url( 

137 request.environ['nova.context']) 

138 glance_url = self._update_glance_link_prefix(glance_url) 

139 return '/'.join([glance_url, 

140 self._collection_name, 

141 str(identifier)]) 

142 

143 @staticmethod 

144 def _format_date(dt): 

145 """Return standard format for a given datetime object.""" 

146 if dt is not None: 146 ↛ exitline 146 didn't return from function '_format_date' because the condition on line 146 was always true

147 return utils.isotime(dt) 

148 

149 @staticmethod 

150 def _get_status(image): 

151 """Update the status field to standardize format.""" 

152 return { 

153 'active': 'ACTIVE', 

154 'queued': 'SAVING', 

155 'saving': 'SAVING', 

156 'deleted': 'DELETED', 

157 'pending_delete': 'DELETED', 

158 'killed': 'ERROR', 

159 }.get(image.get("status"), 'UNKNOWN') 

160 

161 @staticmethod 

162 def _get_progress(image): 

163 return { 

164 "queued": 25, 

165 "saving": 50, 

166 "active": 100, 

167 }.get(image.get("status"), 0)