blob: 84ae8f887eae0420f0997679fbf1571707cc1891 [file] [log] [blame]
Alex Klein2966e302019-01-17 13:29:38 -07001# -*- coding: utf-8 -*-
2# Copyright 2018 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Image API Service.
7
8The image related API endpoints should generally be found here.
9"""
10
11from __future__ import print_function
12
13import os
14
Alex Klein8cb365a2019-05-15 16:24:53 -060015from chromite.api import controller
David Burgerb171d652019-05-13 16:07:00 -060016from chromite.api.gen.chromiumos import common_pb2
Alex Klein4f0eb432019-05-02 13:56:04 -060017from chromite.api.controller import controller_util
18from chromite.lib import cros_build_lib
Alex Klein56355682019-02-07 10:36:54 -070019from chromite.lib import constants
20from chromite.lib import image_lib
Alex Kleinb7cdbe62019-02-22 11:41:32 -070021from chromite.service import image
Alex Klein2966e302019-01-17 13:29:38 -070022
Alex Klein56355682019-02-07 10:36:54 -070023# The image.proto ImageType enum ids.
David Burgerb171d652019-05-13 16:07:00 -060024_BASE_ID = common_pb2.BASE
25_DEV_ID = common_pb2.DEV
26_TEST_ID = common_pb2.TEST
Alex Klein21b95022019-05-09 14:14:46 -060027_BASE_VM_ID = common_pb2.BASE_VM
28_TEST_VM_ID = common_pb2.TEST_VM
Alex Klein56355682019-02-07 10:36:54 -070029
30# Dict to allow easily translating names to enum ids and vice versa.
31_IMAGE_MAPPING = {
32 _BASE_ID: constants.IMAGE_TYPE_BASE,
33 constants.IMAGE_TYPE_BASE: _BASE_ID,
34 _DEV_ID: constants.IMAGE_TYPE_DEV,
35 constants.IMAGE_TYPE_DEV: _DEV_ID,
36 _TEST_ID: constants.IMAGE_TYPE_TEST,
37 constants.IMAGE_TYPE_TEST: _TEST_ID,
38}
39
Alex Klein21b95022019-05-09 14:14:46 -060040_VM_IMAGE_MAPPING = {
41 _BASE_VM_ID: _IMAGE_MAPPING[_BASE_ID],
42 _TEST_VM_ID: _IMAGE_MAPPING[_TEST_ID],
43}
44
Alex Klein56355682019-02-07 10:36:54 -070045
Alex Klein56355682019-02-07 10:36:54 -070046def Create(input_proto, output_proto):
47 """Build an image.
48
49 Args:
50 input_proto (image_pb2.CreateImageRequest): The input message.
51 output_proto (image_pb2.CreateImageResult): The output message.
52 """
53 board = input_proto.build_target.name
54 if not board:
Alex Klein4f0eb432019-05-02 13:56:04 -060055 cros_build_lib.Die('build_target.name is required.')
Alex Klein56355682019-02-07 10:36:54 -070056
Alex Klein56355682019-02-07 10:36:54 -070057 # Build the base image if no images provided.
58 to_build = input_proto.image_types or [_BASE_ID]
Alex Klein56355682019-02-07 10:36:54 -070059
Alex Klein21b95022019-05-09 14:14:46 -060060 image_types, vm_types = _ParseImagesToCreate(to_build)
61 build_config = _ParseCreateBuildConfig(input_proto)
Alex Klein56355682019-02-07 10:36:54 -070062
63 # Sorted isn't really necessary here, but it's much easier to test.
Alex Klein1bcd9882019-03-19 13:25:24 -060064 result = image.Build(board=board, images=sorted(list(image_types)),
65 config=build_config)
Alex Klein56355682019-02-07 10:36:54 -070066
Alex Klein1bcd9882019-03-19 13:25:24 -060067 output_proto.success = result.success
68 if result.success:
69 # Success -- we need to list out the images we built in the output.
70 _PopulateBuiltImages(board, image_types, output_proto)
71 else:
72 # Failure -- include all of the failed packages in the output.
73 for package in result.failed_packages:
74 current = output_proto.failed_packages.add()
75 current.category = package.category
76 current.package_name = package.package
77 if package.version:
78 current.version = package.version
79
Alex Klein8cb365a2019-05-15 16:24:53 -060080 return controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE
Alex Klein1bcd9882019-03-19 13:25:24 -060081
Alex Klein21b95022019-05-09 14:14:46 -060082 if not vm_types:
83 # No VMs to build, we can exit now.
84 return 0
85
86 # There can be only one.
87 vm_type = vm_types.pop()
88 is_test = vm_type == _TEST_VM_ID
89 try:
90 vm_path = image.CreateVm(board, is_test=is_test)
91 except image.ImageToVmError as e:
92 cros_build_lib.Die(e.message)
93
94 new_image = output_proto.images.add()
95 new_image.path = vm_path
96 new_image.type = vm_type
97 new_image.build_target.name = board
98
99
100def _ParseImagesToCreate(to_build):
101 """Helper function to parse the image types to build.
102
103 This function exists just to clean up the Create function.
104
105 Args:
106 to_build (list[int]): The image type list.
107
108 Returns:
109 (set, set): The image and vm types, respectively, that need to be built.
110 """
111 image_types = set()
112 vm_types = set()
113 for current in to_build:
114 if current in _IMAGE_MAPPING:
115 image_types.add(_IMAGE_MAPPING[current])
116 elif current in _VM_IMAGE_MAPPING:
117 vm_types.add(current)
118 # Make sure we build the image required to build the VM.
119 image_types.add(_VM_IMAGE_MAPPING[current])
120 else:
121 # Not expected, but at least it will be obvious if this comes up.
122 cros_build_lib.Die(
123 "The service's known image types do not match those in image.proto. "
124 'Unknown Enum ID: %s' % current)
125
126 if len(vm_types) > 1:
127 cros_build_lib.Die('Cannot create more than one VM.')
128
129 return image_types, vm_types
130
131
132def _ParseCreateBuildConfig(input_proto):
133 """Helper to parse the image build config for Create."""
134 enable_rootfs_verification = not input_proto.disable_rootfs_verification
135 version = input_proto.version or None
136 disk_layout = input_proto.disk_layout or None
137 builder_path = input_proto.builder_path or None
138 return image.BuildConfig(
139 enable_rootfs_verification=enable_rootfs_verification, replace=True,
140 version=version, disk_layout=disk_layout, builder_path=builder_path,
141 )
142
Alex Klein1bcd9882019-03-19 13:25:24 -0600143
144def _PopulateBuiltImages(board, image_types, output_proto):
145 """Helper to list out built images for Create."""
Alex Klein56355682019-02-07 10:36:54 -0700146 # Build out the ImageType->ImagePath mapping in the output.
147 # We're using the default path, so just fetch that, but read the symlink so
148 # the path we're returning is somewhat more permanent.
149 latest_link = image_lib.GetLatestImageLink(board)
Alex Klein4f0eb432019-05-02 13:56:04 -0600150 base_path = os.path.realpath(latest_link)
Alex Klein56355682019-02-07 10:36:54 -0700151
152 for current in image_types:
153 type_id = _IMAGE_MAPPING[current]
154 path = os.path.join(base_path, constants.IMAGE_TYPE_TO_NAME[current])
155
156 new_image = output_proto.images.add()
157 new_image.path = path
158 new_image.type = type_id
Alex Klein4f0eb432019-05-02 13:56:04 -0600159 new_image.build_target.name = board
160
161
162def CreateVm(input_proto, output_proto):
163 """Create a VM from an Image.
164
165 Args:
166 input_proto (image_pb2.CreateVmRequest): The input message.
167 output_proto (image_pb2.CreateVmResponse): The output message.
168 """
169 # TODO(saklein) This currently relies on the build target, but using the image
170 # path directly would be better. Change this to do that when create image
171 # returns an absolute image path rather than chroot relative path.
172 build_target_name = input_proto.image.build_target.name
173 proto_image_type = input_proto.image.type
174
175 if not build_target_name:
176 cros_build_lib.Die('image.build_target.name is required.')
177 if proto_image_type not in _IMAGE_MAPPING:
178 cros_build_lib.Die('Unknown image.type value: %s', proto_image_type)
179
180 chroot = controller_util.ParseChroot(input_proto.chroot)
181 is_test_image = proto_image_type == _TEST_ID
182
183 try:
184 output_proto.vm_image.path = image.CreateVm(build_target_name,
185 chroot=chroot,
186 is_test=is_test_image)
187 except image.Error as e:
188 cros_build_lib.Die(e.message)
Alex Klein2966e302019-01-17 13:29:38 -0700189
190
191def Test(input_proto, output_proto):
192 """Run image tests.
193
194 Args:
195 input_proto (image_pb2.ImageTestRequest): The input message.
196 output_proto (image_pb2.ImageTestResult): The output message.
197 """
198 image_path = input_proto.image.path
199 board = input_proto.build_target.name
200 result_directory = input_proto.result.directory
201
202 if not board:
Alex Klein4f0eb432019-05-02 13:56:04 -0600203 cros_build_lib.Die('The build_target.name is required.')
Alex Klein2966e302019-01-17 13:29:38 -0700204 if not result_directory:
Alex Klein4f0eb432019-05-02 13:56:04 -0600205 cros_build_lib.Die('The result.directory is required.')
Alex Klein2966e302019-01-17 13:29:38 -0700206 if not image_path:
Alex Klein4f0eb432019-05-02 13:56:04 -0600207 cros_build_lib.Die('The image.path is required.')
Alex Klein2966e302019-01-17 13:29:38 -0700208
209 if not os.path.isfile(image_path) or not image_path.endswith('.bin'):
Alex Klein4f0eb432019-05-02 13:56:04 -0600210 cros_build_lib.Die(
Alex Klein2966e302019-01-17 13:29:38 -0700211 'The image.path must be an existing image file with a .bin extension.')
212
Alex Klein8cb365a2019-05-15 16:24:53 -0600213 success = image.Test(board, result_directory, image_dir=image_path)
214 output_proto.success = success
215
216 if success:
217 return controller.RETURN_CODE_SUCCESS
218 else:
219 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY