• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# Copyright 2018 - The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain 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,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16r"""host setup runner
17
18A setup sub task runner to support setting up the local host for AVD local
19instance.
20"""
21
22from __future__ import print_function
23
24import getpass
25import logging
26import os
27import shutil
28import sys
29import tempfile
30
31from acloud.internal import constants
32from acloud.internal.lib import utils
33from acloud.setup import base_task_runner
34from acloud.setup import setup_common
35from acloud.setup import mkcert
36
37
38logger = logging.getLogger(__name__)
39
40_CF_COMMOM_FOLDER = "cf-common"
41
42_INTEL = "intel"
43_AMD = "amd"
44_KVM_INTEL = "kvm_intel"
45_KVM_AMD = "kvm_amd"
46_LIST_OF_INTEL_MODULES = [_KVM_INTEL, "kvm"]
47_LIST_OF_AMD_MODULES = [_KVM_AMD, "kvm"]
48_DICT_MODULES = {_INTEL: _LIST_OF_INTEL_MODULES, _AMD: _LIST_OF_AMD_MODULES}
49_INTEL_COMMANDS = [
50    "sudo rmmod kvm_intel || true", "sudo rmmod kvm || true",
51    "sudo modprobe kvm", "sudo modprobe kvm_intel"]
52_AMD_COMMANDS = [
53    "sudo rmmod kvm_amd || true", "sudo rmmod kvm|| true", "sudo modprobe kvm",
54    "sudo modprobe kvm_amd"]
55_DICT_SETUP_CMDS = {_INTEL: _INTEL_COMMANDS, _AMD: _AMD_COMMANDS}
56_UPDATE_APT_GET_CMD = "sudo apt-get update"
57_INSTALL_CUTTLEFISH_COMMOM_CMD = [
58    "git clone https://github.com/google/android-cuttlefish.git {git_folder}",
59    "cd {git_folder}/base",
60    "debuild -i -us -uc -b",
61    "cd ../frontend",
62    "debuild -i -us -uc -b",
63    "sudo dpkg -i ../cuttlefish-base_*_*64.deb || sudo apt-get install -f",
64    "sudo dpkg -i ../cuttlefish-user_*_*64.deb || sudo apt-get install -f",
65    "sudo dpkg -i ../cuttlefish-common_*_*64.deb || sudo apt-get install -f"]
66_INSTALL_CUTTLEFISH_COMMOM_MSG = ("\nStart to install cuttlefish-common :\n%s"
67                                  "\nEnter 'y' to continue, otherwise N or "
68                                  "enter to exit: ")
69
70
71class BasePkgInstaller(base_task_runner.BaseTaskRunner):
72    """Subtask base runner class for installing packages."""
73
74    # List of packages for child classes to override.
75    PACKAGES = []
76
77    def ShouldRun(self):
78        """Check if required packages are all installed.
79
80        Returns:
81            Boolean, True if required packages are not installed.
82        """
83        if not utils.IsSupportedPlatform():
84            return False
85
86        # Any required package is not installed or not up-to-date will need to
87        # run installation task.
88        for pkg_name in self.PACKAGES:
89            if not setup_common.PackageInstalled(pkg_name):
90                return True
91
92        return False
93
94    def _Run(self):
95        """Install specified packages."""
96        cmd = "\n".join(
97            [setup_common.PKG_INSTALL_CMD % pkg
98             for pkg in self.PACKAGES
99             if not setup_common.PackageInstalled(pkg)])
100
101        if not utils.GetUserAnswerYes("\nStart to install package(s):\n%s"
102                                      "\nEnter 'y' to continue, otherwise N or "
103                                      "enter to exit: " % cmd):
104            sys.exit(constants.EXIT_BY_USER)
105
106        setup_common.CheckCmdOutput(_UPDATE_APT_GET_CMD, shell=True)
107        for pkg in self.PACKAGES:
108            setup_common.InstallPackage(pkg)
109
110        logger.info("All package(s) installed now.")
111
112
113class AvdPkgInstaller(BasePkgInstaller):
114    """Subtask runner class for installing packages for local instances."""
115
116    WELCOME_MESSAGE_TITLE = ("Install required packages for host setup for "
117                             "local instances")
118    WELCOME_MESSAGE = ("This step will walk you through the required packages "
119                       "installation for running Android cuttlefish devices "
120                       "on your host.")
121    PACKAGES = constants.AVD_REQUIRED_PKGS
122
123
124class HostBasePkgInstaller(BasePkgInstaller):
125    """Subtask runner class for installing base host packages."""
126
127    WELCOME_MESSAGE_TITLE = "Install base packages on the host"
128    WELCOME_MESSAGE = ("This step will walk you through the base packages "
129                       "installation for your host.")
130    PACKAGES = constants.BASE_REQUIRED_PKGS
131
132
133class CuttlefishCommonPkgInstaller(base_task_runner.BaseTaskRunner):
134    """Subtask base runner class for installing cuttlefish-common."""
135
136    WELCOME_MESSAGE_TITLE = "Install cuttlefish-common packages on the host"
137    WELCOME_MESSAGE = ("This step will walk you through the cuttlefish-common "
138                       "packages installation for your host.")
139
140    def ShouldRun(self):
141        """Check if cuttlefish-common package is installed.
142
143        Returns:
144            Boolean, True if cuttlefish-common is not installed.
145        """
146        if not utils.IsSupportedPlatform():
147            return False
148
149        # Any required package is not installed or not up-to-date will need to
150        # run installation task.
151        if not setup_common.PackageInstalled(constants.CUTTLEFISH_COMMOM_PKG):
152            return True
153        return False
154
155    def _Run(self):
156        """Install cuttlefilsh-common packages."""
157        if setup_common.IsPackageInAptList(constants.CUTTLEFISH_COMMOM_PKG):
158            cmd = setup_common.PKG_INSTALL_CMD % constants.CUTTLEFISH_COMMOM_PKG
159            if not utils.GetUserAnswerYes(_INSTALL_CUTTLEFISH_COMMOM_MSG % cmd):
160                sys.exit(constants.EXIT_BY_USER)
161            setup_common.InstallPackage(constants.CUTTLEFISH_COMMOM_PKG)
162            return
163
164        # Install cuttlefish-common from github.
165        cf_common_path = os.path.join(tempfile.mkdtemp(), _CF_COMMOM_FOLDER)
166        logger.debug("cuttlefish-common path: %s", cf_common_path)
167        cmd = "\n".join(sub_cmd.format(git_folder=cf_common_path)
168                        for sub_cmd in _INSTALL_CUTTLEFISH_COMMOM_CMD)
169        try:
170            if not utils.GetUserAnswerYes(_INSTALL_CUTTLEFISH_COMMOM_MSG % cmd):
171                sys.exit(constants.EXIT_BY_USER)
172            setup_common.CheckCmdOutput(cmd, shell=True)
173        finally:
174            shutil.rmtree(os.path.dirname(cf_common_path))
175
176
177class LocalCAHostSetup(base_task_runner.BaseTaskRunner):
178    """Subtask class that setup host for setup local CA."""
179
180    WELCOME_MESSAGE_TITLE = "Local CA Host Environment Setup"
181    WELCOME_MESSAGE = ("This step will walk you through the local CA setup "
182                       "to your host for assuring a secure localhost url "
183                       "connection when launching an AVD over webrtc.")
184
185    def ShouldRun(self):
186        """Check if the local CA is setup or not.
187
188        Returns:
189            Boolean, True if local CA is ready.
190        """
191        if not utils.IsSupportedPlatform():
192            return False
193
194        return not mkcert.IsRootCAReady()
195
196    def _Run(self):
197        """Setup host environment for the local CA."""
198        if not utils.GetUserAnswerYes("\nStart to setup the local CA:\n"
199                                      "\nEnter 'y' to continue, otherwise N or "
200                                      "enter to exit: "):
201            sys.exit(constants.EXIT_BY_USER)
202
203        mkcert.Install()
204        logger.info("The local CA '%s.pem' is installed now.",
205                    constants.SSL_CA_NAME)
206
207
208class CuttlefishHostSetup(base_task_runner.BaseTaskRunner):
209    """Subtask class that setup host for cuttlefish."""
210
211    WELCOME_MESSAGE_TITLE = "Host Environment Setup"
212    WELCOME_MESSAGE = (
213        "This step will help you to setup environment for running Android "
214        "cuttlefish devices on your host. That includes adding user to kvm "
215        "related groups and checking required linux modules."
216    )
217
218    def ShouldRun(self):
219        """Check host user groups and modules.
220
221         Returns:
222             Boolean: False if user is in all required groups and all modules
223                      are reloaded.
224         """
225        if not utils.IsSupportedPlatform():
226            return False
227
228        return not (utils.CheckUserInGroups(constants.LIST_CF_USER_GROUPS)
229                    and self._CheckLoadedModules(
230                        _DICT_MODULES.get(self._GetProcessorType())))
231
232    @staticmethod
233    def _GetProcessorType():
234        """Get the processor type.
235
236        Returns:
237            The processor type of the host. e.g. amd, intel.
238        """
239        lsmod_output = setup_common.CheckCmdOutput("lsmod", print_cmd=False)
240        current_modules = [r.split()[0] for r in lsmod_output.splitlines()]
241        if _KVM_AMD in current_modules:
242            return _AMD
243        return _INTEL
244
245    @staticmethod
246    def _CheckLoadedModules(module_list):
247        """Check if the modules are all in use.
248
249        Args:
250            module_list: The list of module name.
251
252        Returns:
253            True if all modules are in use.
254        """
255        logger.info("Checking if modules are loaded: %s", module_list)
256        lsmod_output = setup_common.CheckCmdOutput("lsmod", print_cmd=False)
257        current_modules = [r.split()[0] for r in lsmod_output.splitlines()]
258        all_modules_present = True
259        for module in module_list:
260            if module not in current_modules:
261                logger.info("missing module: %s", module)
262                all_modules_present = False
263        return all_modules_present
264
265    def _Run(self):
266        """Setup host environment for local cuttlefish instance support."""
267        # TODO: provide --uid args to let user use prefered username
268        username = getpass.getuser()
269        setup_cmds = _DICT_SETUP_CMDS.get(self._GetProcessorType())
270        for group in constants.LIST_CF_USER_GROUPS:
271            setup_cmds.append("sudo usermod -aG %s % s" % (group, username))
272
273        print("Below commands will be run:")
274        for setup_cmd in setup_cmds:
275            print(setup_cmd)
276
277        if self._ConfirmContinue():
278            for setup_cmd in setup_cmds:
279                setup_common.CheckCmdOutput(setup_cmd, shell=True)
280            print("Host environment setup has done!")
281
282    @staticmethod
283    def _ConfirmContinue():
284        """Ask user if they want to continue.
285
286        Returns:
287            True if user answer yes.
288        """
289        answer_client = utils.InteractWithQuestion(
290            "\nEnter 'y' to continue, otherwise N or enter to exit: ",
291            utils.TextColors.WARNING)
292        return answer_client in constants.USER_ANSWER_YES
293