| #!/usr/bin/env python |
| # |
| # Copyright 2018 - The Android Open Source Project |
| # |
| # 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. |
| r"""RemoteImageRemoteInstance class. |
| |
| Create class that is responsible for creating a remote instance AVD with a |
| remote image. |
| """ |
| |
| import logging |
| import re |
| import subprocess |
| import time |
| |
| from acloud.create import base_avd_create |
| from acloud.internal import constants |
| from acloud.internal.lib import oxygen_client |
| from acloud.internal.lib import utils |
| from acloud.public.actions import common_operations |
| from acloud.public.actions import remote_instance_cf_device_factory |
| from acloud.public import report |
| |
| |
| logger = logging.getLogger(__name__) |
| _DEVICE = "device" |
| _DEVICES = "devices" |
| _LAUNCH_CVD_TIME = "launch_cvd_time" |
| _RE_SESSION_ID = re.compile(r".*session_id:\"(?P<session_id>[^\"]+)") |
| _RE_SERVER_URL = re.compile(r".*server_url:\"(?P<server_url>[^\"]+)") |
| _RE_OXYGEN_LEASE_ERROR = re.compile( |
| r".*Error received while trying to lease device: (?P<error>.*)$", re.DOTALL) |
| |
| |
| class RemoteImageRemoteInstance(base_avd_create.BaseAVDCreate): |
| """Create class for a remote image remote instance AVD.""" |
| |
| @utils.TimeExecute(function_description="Total time: ", |
| print_before_call=False, print_status=False) |
| def _CreateAVD(self, avd_spec, no_prompts): |
| """Create the AVD. |
| |
| Args: |
| avd_spec: AVDSpec object that tells us what we're going to create. |
| no_prompts: Boolean, True to skip all prompts. |
| |
| Returns: |
| A Report instance. |
| """ |
| if avd_spec.oxygen: |
| return self._LeaseOxygenAVD(avd_spec) |
| device_factory = remote_instance_cf_device_factory.RemoteInstanceDeviceFactory( |
| avd_spec) |
| create_report = common_operations.CreateDevices( |
| "create_cf", avd_spec.cfg, device_factory, avd_spec.num, |
| report_internal_ip=avd_spec.report_internal_ip, |
| autoconnect=avd_spec.autoconnect, |
| avd_type=constants.TYPE_CF, |
| boot_timeout_secs=avd_spec.boot_timeout_secs, |
| unlock_screen=avd_spec.unlock_screen, |
| wait_for_boot=False, |
| connect_webrtc=avd_spec.connect_webrtc, |
| client_adb_port=avd_spec.client_adb_port) |
| if create_report.status == report.Status.SUCCESS: |
| if avd_spec.connect_vnc: |
| utils.LaunchVNCFromReport(create_report, avd_spec, no_prompts) |
| if avd_spec.connect_webrtc: |
| utils.LaunchBrowserFromReport(create_report) |
| |
| return create_report |
| |
| def _LeaseOxygenAVD(self, avd_spec): |
| """Lease the AVD from the AVD pool. |
| |
| Args: |
| avd_spec: AVDSpec object that tells us what we're going to create. |
| |
| Returns: |
| A Report instance. |
| """ |
| timestart = time.time() |
| session_id = None |
| server_url = None |
| try: |
| response = oxygen_client.OxygenClient.LeaseDevice( |
| avd_spec.remote_image[constants.BUILD_TARGET], |
| avd_spec.remote_image[constants.BUILD_ID], |
| avd_spec.remote_image[constants.BUILD_BRANCH], |
| avd_spec.system_build_info[constants.BUILD_TARGET], |
| avd_spec.system_build_info[constants.BUILD_ID], |
| avd_spec.kernel_build_info[constants.BUILD_TARGET], |
| avd_spec.kernel_build_info[constants.BUILD_ID], |
| avd_spec.cfg.oxygen_client, |
| avd_spec.cfg.oxygen_lease_args) |
| session_id, server_url = self._GetDeviceInfoFromResponse(response) |
| execution_time = round(time.time() - timestart, 2) |
| except subprocess.CalledProcessError as e: |
| logger.error("Failed to lease device from Oxygen, error: %s", |
| e.output) |
| response = e.output |
| |
| reporter = report.Report(command="create_cf") |
| if session_id and server_url: |
| reporter.SetStatus(report.Status.SUCCESS) |
| device_data = {"instance_name": session_id, |
| "ip": server_url} |
| device_data[_LAUNCH_CVD_TIME] = execution_time |
| dict_devices = {_DEVICES: [device_data]} |
| reporter.UpdateData(dict_devices) |
| else: |
| # Try to parse client error |
| match = _RE_OXYGEN_LEASE_ERROR.match(response) |
| if match: |
| response = match.group("error").strip() |
| |
| reporter.SetStatus(report.Status.FAIL) |
| reporter.SetErrorType(constants.ACLOUD_OXYGEN_LEASE_ERROR) |
| reporter.AddError(response) |
| |
| return reporter |
| |
| @staticmethod |
| def _GetDeviceInfoFromResponse(response): |
| """Get session id and server url from response. |
| |
| Args: |
| response: String of the response from oxygen proxy client. |
| e.g. "2021/08/02 11:28:52 session_id: "74b6b835" |
| server_url: "0.0.0.34" port:{type:WATERFALL ..." |
| |
| Returns: |
| The session id and the server url of leased device. |
| """ |
| session_id = "" |
| for line in response.splitlines(): |
| session_id_match = _RE_SESSION_ID.match(line) |
| if session_id_match: |
| session_id = session_id_match.group("session_id") |
| break |
| |
| server_url = "" |
| for line in response.splitlines(): |
| server_url_match = _RE_SERVER_URL.match(line) |
| if server_url_match: |
| server_url = server_url_match.group("server_url") |
| break |
| return session_id, server_url |