Setup users host env for local instance support

- Setup flow controller in setup.py
- Define common method in base_task_runner.py
- Required package installer in host_setup_runner.py
- Host env configuration in host_setup_runner.py

Bug: 110388133
Test: ./run_tests.sh, m acloud && acloud setup.
Change-Id: Id8b988149d606553d31dfb466e0ae14883513553
diff --git a/setup/host_setup_runner.py b/setup/host_setup_runner.py
new file mode 100644
index 0000000..569a16d
--- /dev/null
+++ b/setup/host_setup_runner.py
@@ -0,0 +1,195 @@
+#!/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"""host setup runner
+
+A setup sub task runner to support setting up the local host for AVD local
+instance.
+"""
+
+from __future__ import print_function
+
+import getpass
+import grp
+import logging
+import os
+import platform
+
+from acloud.internal import constants
+from acloud.internal.lib import utils
+from acloud.setup import base_task_runner
+from acloud.setup import setup_common
+
+logger = logging.getLogger(__name__)
+
+# Install cuttlefish-common will probably not work now.
+# TODO: update this to pull from the proper repo.
+_CUTTLEFISH_REQUIRED_PKGS = ["cuttlefish-common"]
+# dict of supported system and their distributions.
+_SUPPORTED_SYSTEMS_AND_DISTS = {"Linux": ["Ubuntu", "Debian"]}
+_LIST_OF_GROUPS = ["kvm", "libvirt", "cvdnetwork"]
+_LIST_OF_MODULES = ["kvm_intel", "kvm"]
+
+
+def _IsSupportedPlatform():
+    """Check if user's os is the supported platform.
+
+    Returns:
+        Boolean, True if user is using supported platform.
+    """
+    system = platform.system()
+    dist = platform.linux_distribution()[0]
+    platform_supported = (system in _SUPPORTED_SYSTEMS_AND_DISTS and
+                          dist in _SUPPORTED_SYSTEMS_AND_DISTS[system])
+
+    logger.info("supported system and dists: %s",
+                _SUPPORTED_SYSTEMS_AND_DISTS)
+    logger.info("%s[%s] %s supported platform",
+                system,
+                dist,
+                "is a" if platform_supported else "is not a")
+
+    return platform_supported
+
+
+class CuttlefishPkgInstaller(base_task_runner.BaseTaskRunner):
+    """Subtask runner class for installing required packages."""
+
+    WELCOME_MESSAGE_TITLE = "Install required package for host setup"
+    WELCOME_MESSAGE = (
+        "This step will walk you through the required packages installation for "
+        "running Android cuttlefish devices on your host.")
+
+    def ShouldRun(self):
+        """Check if required packages are all installed.
+
+        Returns:
+            Boolean, True if required packages are not installed.
+        """
+        if not _IsSupportedPlatform():
+            return False
+
+        # Any required package is not installed or not up-to-date will need to
+        # run installation task.
+        for pkg_name in _CUTTLEFISH_REQUIRED_PKGS:
+            if not setup_common.PackageInstalled(pkg_name):
+                return True
+
+        return False
+
+    def _Run(self):
+        """Install Cuttlefish-common package."""
+
+        logger.info("Start to install required package: %s ",
+                    _CUTTLEFISH_REQUIRED_PKGS)
+
+        for pkg in _CUTTLEFISH_REQUIRED_PKGS:
+            setup_common.InstallPackage(pkg)
+
+        logger.info("All required package are installed now.")
+
+
+class CuttlefishHostSetup(base_task_runner.BaseTaskRunner):
+    """Subtask class that setup host for cuttlefish."""
+
+    WELCOME_MESSAGE_TITLE = "Host Enviornment Setup"
+    WELCOME_MESSAGE = (
+        "This step will help you to setup enviornment for running Android "
+        "cuttlefish devices on your host. That includes adding user to kvm "
+        "related groups and checking required linux modules."
+    )
+
+    def ShouldRun(self):
+        """Check host user groups and modules.
+
+         Returns:
+             Boolean: False if user is in all required groups and all modules
+                      are reloaded.
+         """
+        if not _IsSupportedPlatform():
+            return False
+
+        return not (self._CheckUserInGroups(_LIST_OF_GROUPS)
+                    and self._CheckLoadedModules(_LIST_OF_MODULES))
+
+    @staticmethod
+    def _CheckUserInGroups(group_name_list):
+        """Check if the current user is in the group.
+
+        Args:
+            group_name_list: The list of group name.
+        Returns:
+            True if current user is in all the groups.
+        """
+        logger.info("Checking if user is in following groups: %s", group_name_list)
+        current_groups = [grp.getgrgid(g).gr_name for g in os.getgroups()]
+        all_groups_present = True
+        for group in group_name_list:
+            if group not in current_groups:
+                all_groups_present = False
+                logger.info("missing group: %s", group)
+        return all_groups_present
+
+    @staticmethod
+    def _CheckLoadedModules(module_list):
+        """Check if the modules are all in use.
+
+        Args:
+            module_list: The list of module name.
+        Returns:
+            True if all modules are in use.
+        """
+        logger.info("Checking if modules are loaded: %s", module_list)
+        lsmod_output = setup_common.CheckCmdOutput("lsmod", print_cmd=False)
+        current_modules = [r.split()[0] for r in lsmod_output.splitlines()]
+        all_modules_present = True
+        for module in module_list:
+            if module not in current_modules:
+                logger.info("missing module: %s", module)
+                all_modules_present = False
+        return all_modules_present
+
+    def _Run(self):
+        """Setup host environment for local cuttlefish instance support."""
+        # TODO: provide --uid args to let user use prefered username
+        username = getpass.getuser()
+        setup_cmds = [
+            "sudo rmmod kvm_intel",
+            "sudo rmmod kvm",
+            "sudo modprobe kvm",
+            "sudo modprobe kvm_intel"]
+        for group in _LIST_OF_GROUPS:
+            setup_cmds.append("sudo usermod -aG %s % s" % (group, username))
+
+        print("Below commands will be run:")
+        for setup_cmd in setup_cmds:
+            print(setup_cmd)
+
+        if self._ConfirmContinue():
+            for setup_cmd in setup_cmds:
+                setup_common.CheckCmdOutput(setup_cmd, shell=True)
+            print("Host environment setup has done!")
+
+    @staticmethod
+    def _ConfirmContinue():
+        """Ask user if they want to continue.
+
+        Returns:
+            True if user answer yes.
+        """
+        answer_client = utils.InteractWithQuestion(
+            "\nPress 'y' to continue or anything else to do it myself:[y]",
+            utils.TextColors.WARNING)
+        return answer_client in constants.USER_ANSWER_YES