Ejemplo n.º 1
0
    def run(self,
            script=None,
            hosts=None,
            username=None,
            processors=4,
            verbose=False):

        results = []

        if type(hosts) != list:
            hosts = Parameter.expand(hosts)

        for command in script.splitlines():
            print(hosts, "->", command)
            if command.startswith("#") or command.strip() == "":
                pass
                # print (command)
            elif len(hosts) == 1 and hosts[0] == self.hostname:
                os.system(command)
            elif len(hosts) == 1 and hosts[0] != self.hostname:
                host = hosts[0]
                os.system(f"ssh {host} {command}")
            else:
                result = Host.ssh(hosts=hosts,
                                  command=command,
                                  username=username,
                                  key="~/.ssh/id_rsa.pub",
                                  processors=processors,
                                  executor=os.system)
                results.append(result)
        if verbose:
            pprint(results)
            for result in results:
                print(Printer.write(result, order=['host', 'stdout']))
        return results
Ejemplo n.º 2
0
    def _ssh(self, command, timeout=1):
        """
        An internal ssh command that allows to ssh into a remote host while having a small timeout value

        Args:
            command (str): the command to be executed
            timeout (int): the number of seconds for a timeout

        Returns:
            the result of the executed command as a string.
            It ignores errors and includes them into the result string.

        """
        _command = f"ssh -o ConnectTimeout={timeout} -o StrictHostKeyChecking=no {command}"
        # print (_command)
        if os_is_windows:
            import subprocess
            hostname = subprocess.run(['hostname'],
                                      capture_output=True,
                                      text=True).stdout.strip()
            results = Host.ssh(hosts=hostname, command=_command)
            print(Printer.write(results))
            for entry in results:
                print(str(entry["stdout"]))
                r = str(entry["stdout"])
        else:
            r = Shell.run(_command).strip()
        return r
Ejemplo n.º 3
0
    def ping(self, processors=1):
        StopWatch.start(f"total p={processors} c=1")
        r = Host.ping(hosts, processors=processors, count=1)
        StopWatch.stop(f"total p={processors} c=1")
        StopWatch.status(f"total p={processors} c=1", r[0]['success'])

        return r
Ejemplo n.º 4
0
    def ssh(self, processors=1):
        StopWatch.start(f"total p={processors} c=1")
        r = Host.ssh(hosts, command="hostname", processors=processors)
        StopWatch.stop(f"total p={processors} c=1")
        StopWatch.status(f"total p={processors} c=1", r[0]["success"])

        return r
Ejemplo n.º 5
0
    def test_gather_keys(self):
        HEADING()
        hosts = Parameter.expand("red[01-03]")
        result = CommonHost.gather_keys(hosts=hosts)

        pprint(result)

        assert len(result) == len(hosts)
Ejemplo n.º 6
0
    def test_ssh_config(self):
        HEADING()

        results = CommonHost.ssh(hosts="red[01-03]", command="uname")
        pprint(results)

        for result in results:
            assert result.stdout.strip().lower() == sys.platform
Ejemplo n.º 7
0
    def __init__(self,
                 inventory=None,
                 names=None,
                 ssid=None,
                 wifipassword=None,
                 force_inv=False,
                 country=None):
        # Get inventory
        self.ssid = ssid
        self.wifipasswd = wifipassword
        if inventory is None:
            names = Parameter.expand(names)
            manager, workers = Host.get_hostnames(names)
            if workers:
                worker_base_name = ''.join(
                    [i for i in workers[0] if not i.isdigit()])

            cluster_name = manager or worker_base_name
            inventory = path_expand(
                f'~/.cloudmesh/inventory-{cluster_name}.yaml')

            if not os.path.exists(inventory) or force_inv:
                if not manager:
                    Console.error("No inventory found. Can not create an "
                                  "inventory without a "
                                  "manager.")
                    return ""

                Inventory.build_default_inventory(filename=inventory,
                                                  manager=manager,
                                                  workers=workers)
            inv = Inventory(filename=inventory)

        else:
            inv = Inventory(filename=inventory)
        self.inventory = inv

        # Find managers and workers
        managers = inv.find(service='manager')
        if len(managers) > 0:
            if not self.ssid:
                self.ssid = get_ssid()
                if self.ssid == "":
                    Console.info('Could not determine SSID, skipping wifi '
                                 'config')
                    self.ssid = None
            if not self.wifipasswd and self.ssid:
                self.wifipasswd = getpass(f"Using --SSID={self.ssid}, please "
                                          f"enter wifi password:")
        workers = inv.find(service='worker')
        # No inherenet need to distinguish the configs by service
        configs = managers + workers
        # Create dict for them for easy lookup
        self.configs = dict((config['host'], config) for config in configs)
        self.get_images()
        self.country = country if country else Shell.locale().upper()
Ejemplo n.º 8
0
    def reset_remote(
        hosts=None,
        username=None,
        processors=3):

        command = f"echo mmc0 >/sys/class/leds/led0/trigger"
        result = Host.ssh(hosts=hosts,
                          command=command,
                          username=username,
                          key="~/.ssh/id_rsa.pub",
                          processors=processors,
                          executor=os.system)
Ejemplo n.º 9
0
    def test_ssh_keygen(self):
        HEADING()

        hosts = Parameter.expand("red[01-03]")
        for host in hosts:
            banner(host)
            result = CommonHost.generate_key(hosts=host,
                                             filename=f"~/.ssh/id_rsa_{host}",
                                             dryrun=False)
            #time.sleep(1)
            banner("result", c="-")
            pprint(result)
Ejemplo n.º 10
0
    def test_ping(self):
        HEADING()

        try:
            result = CommonHost.ping(hosts="red[01-03]")
            pprint(result)

        except Exception as e:
            Console.error("Make sure you have the following in your ~/.ssh/config")
            print(config_hosts)
            Console.error(
                "Make sure you have the ssh remote login enabled")
            print(e)
            Console.error("Your computer is not properly set up to do the test")
            sys.exit()
Ejemplo n.º 11
0
    def __init__(self, hostname=None, ip=None, dryrun=False, no_diagram=False):

        self.dryrun = dryrun or False
        self.hostnames_str = hostname
        self.ips_str = ip
        self.hostnames = hostnames = hostname or "red,red[01-02]"
        self.ips = ips = ip or "10.0.0.[1-3]"
        self.ssid = get_ssid()
        self.imaged = ""
        self.wifipassword = ""
        self.no_diagram = no_diagram

        hostnames = Parameter.expand(hostnames)
        manager, workers = Host.get_hostnames(hostnames)

        if workers is None:
            n = 1
        else:
            n = len(workers) + 1
        if ip is None:
            ips = Parameter.expand(f"10.1.1.[1-{n}]")
        else:
            ips = Parameter.expand(ips)

        # cluster_hosts = tuple(zip(ips, hostnames))

        self.key = path_expand("~/.ssh/id_rsa.pub")

        banner("Parameters", figlet=True)

        print("Manager:      ", manager)
        print("Workers:      ", workers)
        print("IPS:          ", ips)
        print("Key:          ", self.key)
        print("Dryrun:       ", self.dryrun)

        self.manager = manager
        self.workers = workers
        self.ips = ips

        self.load_data()

        if not self.no_diagram:
            self.create_diag(self.manager)
        self.create_layout()
        # sg.change_look_and_feel('SystemDefault')
        self.window = sg.Window('Cloudmesh Pi Burn', self.layout, resizable=True, size=window_size)
Ejemplo n.º 12
0
    def list_remote(
        hosts=None,
        username=None,
        processors=3):

        command = f"cat" \
                  " /sys/class/leds/led0/brightness" \
                  " /sys/class/leds/led1/brightness"
        results = Host.ssh(hosts=hosts,
                           command=command,
                           username=username,
                           key="~/.ssh/id_rsa.pub",
                           processors=processors,
                           executor=os.system)
        for result in results:
            result["green"], result["red"] = result["stdout"].split("\n", 1)

        return results
Ejemplo n.º 13
0
    def test_internal_ping(self):
        HEADING()
        StopWatch.start("total _ping")

        for host in hosts:
            location = {
                'ip': host,
                'count': 1,
            }

            StopWatch.start(f"ping {host}")
            result = Host._ping(location)
            StopWatch.stop(f"ping {host}")

            StopWatch.stop("total _ping")
            if b'Access denied' in result['stdout'] and sys.platform == "win32":
                print("ERROR: This test must be run in an administrative "
                      "terminal")
            assert result['success']
Ejemplo n.º 14
0
    def test_internal_ssh(self):
        print()
        StopWatch.start("total _ssh")

        for host in hosts:
            location = {
                'host': host,
                'username': os.environ['USER'],
                'key': '~/.ssh/id_rsa.pub',
                'command': 'uname -a'
            }

            StopWatch.start(f"ssh {host}")
            result = Host._ssh(location)
            StopWatch.stop(f"ssh {host}")

            StopWatch.stop("total _ssh")

            assert result.success
Ejemplo n.º 15
0
    def set_remote(
        led=None,
        value=1,
        hosts=None,
        username=None,
        processors=3):

        if led not in [1, 0]:
            raise ValueError("Led number is wrong")

        state = LED.get_state(value)

        command = f"echo {state} |" \
                  f" sudo tee /sys/class/leds/led{led}/brightness" \
                  f" >> /dev/null"
        print ("command", command)
        result = Host.ssh(hosts=hosts,
                          command=command,
                          username=username,
                          key="~/.ssh/id_rsa.pub",
                          processors=processors,
                          executor=os.system)
        return result
Ejemplo n.º 16
0
        raise NotImplementedError
        # use the calls above for a single easy to use method

    def configure_worker(self):
        raise NotImplementedError
        # use the calls above for a single easy to use method

    #
    # POST INSTALATION
    #
    def firmware(self):
        raise NotImplementedError


if __name__ == "__main__":
    hostnames = Parameter.expand("red,red[01-02]")
    ips = Parameter.expand("10.0.0.[1-3]")
    manager, workers = Host.get_hostnames(hostnames)

    cloudinit = Cloudinit()
    cloudinit.hostname(manager)
    cloudinit.etc_hosts()  # manager, workers, IPS, worker
    cloudinit.update()
    cloudinit.keyboard()  # locale as parameter
    cloudinit.enable_ssh()
    # cloudinit.register()
    print("cloudinit")
    #
    # ADD WHAT IS NEEDED
    print(cloudinit)
Ejemplo n.º 17
0
    def do_burn(self, args, arguments):
        """
        ::

            Usage:
              burn gui [--hostname=HOSTNAME]
                       [--ip=IP]
                       [--ssid=SSID]
                       [--wifipassword=PSK]
                       [--bs=BLOCKSIZE]
                       [--dryrun]
                       [--no_diagram]
              burn ubuntu NAMES [--inventory=INVENTORY] [--ssid=SSID] [-f]
              [--wifipassword=PSK] [-v] --device=DEVICE [--country=COUNTRY]
              [--upgrade]
              burn raspberry NAMES --device=DEVICE
                                  [--inventory=INVENTORY]
                                  [--ssid=SSID]
                                  [--wifipassword=PSK]
                                  [--country=COUNTRY]
                                  [--password=PASSWORD]
                                  [-v]
                                  [-f]
              burn firmware check
              burn firmware update
              burn install
              burn load --device=DEVICE
              burn format --device=DEVICE
              burn imager [TAG...]
              burn mount [--device=DEVICE] [--os=OS]
              burn unmount [--device=DEVICE] [--os=OS]
              burn network list [--ip=IP] [--used]
              burn network
              burn info [--device=DEVICE]
              burn image versions [--details] [--refresh] [--yaml]
              burn image ls
              burn image delete [--image=IMAGE]
              burn image get [--url=URL] [TAG...]
              burn backup [--device=DEVICE] [--to=DESTINATION]
              burn copy [--device=DEVICE] [--from=DESTINATION]
              burn shrink [--image=IMAGE]
              burn cluster --device=DEVICE --hostname=HOSTNAME
                           [--burning=BURNING]
                           [--ip=IP]
                           [--ssid=SSID]
                           [--wifipassword=PSK]
                           [--bs=BLOCKSIZE]
                           [--os=OS]
                           [-y]
                           [--imaged]
                           [--set_passwd]
              burn create [--image=IMAGE]
                          [--device=DEVICE]
                          [--burning=BURNING]
                          [--hostname=HOSTNAME]
                          [--ip=IP]
                          [--sshkey=KEY]
                          [--blocksize=BLOCKSIZE]
                          [--passwd=PASSWD]
                          [--ssid=SSID]
                          [--wifipassword=PSK]
                          [--format]
                          [--tag=TAG]
                          [--inventory=INVENTORY]
                          [--name=NAME]
                          [-y]
              burn sdcard [TAG...] [--device=DEVICE] [-y]
              burn set [--hostname=HOSTNAME]
                       [--ip=IP]
                       [--key=KEY]
                       [--keyboard=COUNTRY]
                       [--cmdline=CMDLINE]
              burn enable ssh
              burn wifi --ssid=SSID [--passwd=PASSWD] [--country=COUNTRY]
              burn check [--device=DEVICE]
              burn mac --hostname=HOSTNAME



            Options:
              -h --help              Show this screen.
              --version              Show version.
              --image=IMAGE          The image filename,
                                     e.g. 2019-09-26-raspbian-buster.img
              --device=DEVICE        The device, e.g. /dev/sdX
              --hostname=HOSTNAME    The hostnames of the cluster
              --ip=IP                The IP addresses of the cluster
              --key=KEY              The name of the SSH key file
              --blocksize=BLOCKSIZE  The blocksise to burn [default: 4M]
              --burning=BURNING      The hosts to be burned

            Arguments:
               TAG                   Keyword tags to identify an image

            Files:
              This is not fully thought through and needs to be documented
              ~/.cloudmesh/images
                Location where the images will be stored for reuse

            Description:
                cms burn create --inventory=INVENTORY --device=DEVICE --name=NAME

                    Will refer to a specified cloudmesh inventory file (see cms help inventory).
                    Will search the configurations for NAME inside of INVENTORY and will burn
                    to DEVICE. Supports parameter expansion.

                cms burn create --passwd=PASSWD

                     if the passwd flag is added the default password is
                     queried from the commandline and added to all SDCards

                     if the flag is omitted login via the password is
                     disabled and only login via the sshkey is allowed

              Network

                cms burn network list

                    Lists the ip addresses that are on the same network

                     +------------+---------------+----------+-----------+
                     | Name       | IP            | Status   | Latency   |
                     |------------+---------------+----------+-----------|
                     | Router     | 192.168.1.1   | up       | 0.0092s   |
                     | iPhone     | 192.168.1.4   | up       | 0.061s    |
                     | red01      | 192.168.1.46  | up       | 0.0077s   |
                     | laptop     | 192.168.1.78  | up       | 0.058s    |
                     | unkown     | 192.168.1.126 | up       | 0.14s     |
                     | red03      | 192.168.1.158 | up       | 0.0037s   |
                     | red02      | 192.168.1.199 | up       | 0.0046s   |
                     | red        | 192.168.1.249 | up       | 0.00021s  |
                     +------------+----------------+----------+-----------+

                cms burn network list [--used]

                    Lists the used ip addresses as a comma separated parameter
                    list

                       192.168.50.1,192.168.50.4,...

                cms burn network address

                    Lists the own network address

                     +---------+----------------+----------------+
                     | Label   | Local          | Broadcast      |
                     |---------+----------------+----------------|
                     | wlan0   | 192.168.1.12   | 192.168.1.255  |
                     +---------+----------------+----------------+

                cms burn firmware check

                    Checks if the firmware on the Pi is up to date

                cms burn firmware update

                    Checks and updates the firmware on the Pi

                cms burn install

                    Installs a program to shrink img files. THis is
                    useful, after you created a backup to make the
                    backup smaller and allow faster burning in case of
                    recovery

                    This command is not supported on MacOS

                cms burn load --device=DEVICE

                    Loads the sdcard into the USB drive. Thi sis similar to
                    loading a cdrom drive. It s the opposite to eject

                cms burn format --device=DEVICE

                    Formats the SDCard in the specified device. Be
                    careful it is the correct device.  cms burn info
                    will help you to identifying it

                cms burn mount [--device=DEVICE] [--os=OS]

                    Mounts the file systems available on the SDCard

                cms burn unmount [--device=DEVICE] [--os=OS]

                    Unmounts the mounted file systems from the SDCard

                cms burn info [--device=DEVICE]

                    Provides useful information about the SDCard

                cms burn image versions [--refresh] [--yaml]

                    The images that you like to burn onto your SDCard
                    can be cached locally with the image command.  The
                    available images for the PI can be found when
                    using the --refresh option. If you do not specify
                    it it reads a copy of the image list from our
                    cache

                cms burn image ls

                    Lists all downloaded images in our cache. You can
                    download them with the cms burn image get command

                cms burn image delete [--image=IMAGE]

                    Deletes the specified image. The name can be found
                    with the image ls command

                cms burn image get [--url=URL] [TAG...]

                    Downloads a specific image or the latest
                    image. The tag are a number of words separated by
                    a space that must occur in the tag that you find
                    in the versions command

                cms burn backup [--device=DEVICE] [--to=DESTINATION]

                    This command requires you to install pishrink previously with

                        cms burn install

                    Backs up a SDCard to the given location.

                cms burn copy [--device=DEVICE] [--from=DESTINATION]

                    Copies the file form the destination on the SDCard
                    this is the same as the SDCard command. we will in
                    future remove one

                cms burn shrink [--image=IMAGE]

                    Shrinks the size of a backup or image file that
                    is on your local file system. It can only be used
                    for .img files

                   This command is not supported on MacOS.

                cms burn create [--image=IMAGE]
                                [--device=DEVICE]
                                [--hostname=HOSTNAME]
                                [--ip=IP]
                                [--sshkey=KEY]
                                [--blocksize=BLOCKSIZE]
                                [--passwd=PASSWD]
                                [--ssid=SSID]
                                [--wifipassword=PSK]
                                [--format]

                    This command  not only can format the SDCard, but
                    also initializes it with specific values

                cms burn sdcard [TAG...] [--device=DEVICE]

                    this burns the sd card, see also copy and create

                cms burn set [--hostname=HOSTNAME]
                             [--ip=IP]
                             [--key=KEY]
                             [--mount=MOUNTPOINT]
                             [--keyboard=COUNTRY]
                             [--cmdline=CMDLINE]

                    Sets specific values on the sdcard after it
                    has ben created with the create, copy or sdcard
                    command

                    a --ssh is missing from this command

                cms burn enable ssh [--mount=MOUNTPOINT]

                    Enables the ssh server once it is booted

                cms burn wifi --ssid=SSID [--passwd=PASSWD] [--country=COUNTRY]

                    Sets the wifi ssid and password after the card
                    is created, copied, or the sdcard is used.

                    The option country option expects an ISO 3166-1
                    two digit country code. The default is "US" and
                    the option not required if suitable. See
                    https://en.wikipedia.org/wiki/ISO_3166-1 for other
                    countries.

                cms burn check [--device=DEVICE]

                    Lists the parameters that were set
                    with the set or create command

            Examples: ( \\ is not shown)

               > cms burn create --image=2019-09-26-raspbian-buster-lite
               >                 --device=/dev/mmcblk0
               >                 --hostname=red[5-7]
               >                 --ip=192.168.1.[5-7]
               >                 --sshkey=id_rsa

               > cms burn image get latest

               > cms burn image get https://downloads.raspberrypi.org/
               >   raspbian_lite/images/
               >   raspbian_lite-2018-10-11/2018-10-09-raspbian-stretch-lite.zip

               > cms burn image delete 2019-09-26-raspbian-buster-lite

        """
        map_parameters(arguments, "details", "refresh", "device", "dryrun",
                       "burning", "hostname", "ip", "sshkey", "blocksize",
                       "ssid", "url", "imaged", "key", "keyboard", "passwd",
                       "wifipassword", "version", "to", "os", "country",
                       "inventory", "name", "bs", "set_passwd", "cmdline",
                       "upgrade", "no_diagram")

        # arguments.MOUNTPOINT = arguments["--mount"]
        arguments.FORMAT = arguments["--format"]
        arguments.FROM = arguments["--from"]
        arguments.IMAGE = arguments["--image"]
        arguments.output = "table"  # hard code for now
        arguments.bs = arguments.bs or "4M"
        arguments.yes = arguments["-y"]
        if len(arguments.TAG) == 0:
            arguments.TAG = "latest"

        # VERBOSE(arguments)

        def execute(label, function):
            StopWatch.start(label)
            result = function
            StopWatch.stop(label)
            StopWatch.status(label, True)
            return result

        burner = Burner()
        sdcard = SDCard()

        if arguments.imager:

            arguments.TAG = arguments.TAG or ["latest-lite"]

            Console.msg(f"Tags: {arguments.TAG}")
            try:
                file = Imager.fetch(tag=arguments.TAG)
            except:  # noqa: E722
                pass

            try:
                Imager.launch(file=file)
            except Exception as e:
                Console.error(
                    f"could not find image with the tag {arguments.TAG}\n\n{e}\n"
                )

            return ""

        elif arguments.gui:

            from cloudmesh.burn.gui import Gui
            VERBOSE(arguments)
            g = Gui(hostname=arguments.hostname,
                    ip=arguments.ip,
                    dryrun=arguments.dryrun,
                    no_diagram=arguments.no_diagram)

            g.run()

            return ""

        elif arguments.raspberry:

            banner(txt="RaspberryOS Burn", figlet=True)

            if arguments.inventory:
                inv_path = path_expand(f'~/.cloudmesh/{arguments.inventory}')
                try:
                    burner = RaspberryBurner(
                        inventory=inv_path,
                        ssid=arguments['--ssid'],
                        wifipassword=arguments['--wifipassword'],
                        country=arguments['--country'])
                except:
                    Console.error('Burner Error')
                    return ""
            else:
                try:
                    burner = RaspberryBurner(
                        names=arguments.NAMES,
                        ssid=arguments['--ssid'],
                        wifipassword=arguments['--wifipassword'],
                        force_inv=arguments['-f'],
                        country=arguments['--country'])
                except Exception as e:
                    Console.error('Burner Error')
                    raise e

                    return ""

            execute(
                "burn raspberry",
                burner.multi_burn(
                    names=arguments.NAMES,
                    devices=arguments.device,
                    verbose=arguments['-v'],
                    password=arguments['--password'],
                ))
            return ""

        elif arguments.ubuntu:
            banner(txt="Ubuntu Burn with cloud-init", figlet=True)
            names = Parameter.expand(arguments.NAMES)
            if len(Parameter.expand(arguments.device)) > 1:
                Console.error(
                    "Too many devices specified. Please only specify one")
                return ""

            if arguments.inventory:
                c = Configure(inventory=arguments.inventory,
                              debug=arguments['-v'])
                inv = Inventory(filename=arguments.inventory)
            else:
                names = Parameter.expand(arguments.NAMES)
                manager, workers = Host.get_hostnames(names)
                if workers:
                    worker_base_name = ''.join(
                        [i for i in workers[0] if not i.isdigit()])

                cluster_name = manager or worker_base_name
                inventory = path_expand(
                    f'~/.cloudmesh/inventory-{cluster_name}.yaml')

                if not os.path.exists(inventory) or arguments['-f']:
                    if not manager:
                        Console.error("No inventory found. Can not create an "
                                      "inventory without a "
                                      "manager.")
                        return ""

                    Inventory.build_default_inventory(
                        filename=inventory,
                        manager=manager,
                        workers=workers,
                        manager_image='ubuntu-20.10-64-bit',
                        worker_image='ubuntu-20.10-64-bit')

                c = Configure(inventory=inventory,
                              debug=arguments['-v'],
                              download_images=True)
                inv = Inventory(filename=inventory)

            names = Parameter.expand(arguments.NAMES)
            manager, workers = Host.get_hostnames(names)
            if manager:
                if not arguments.ssid and 'wifi' in c.configs[manager][
                        'services']:
                    arguments.ssid = get_ssid()
                    if arguments.ssid == "":
                        Console.info('Could not determine SSID, skipping wifi '
                                     'config')
                        arguments.ssid = None
                if not arguments.wifipassword and arguments.ssid is not None:
                    arguments.country = Shell.locale().upper()
                    arguments.wifipassword = getpass(
                        f"Using --SSID="
                        f"{arguments.ssid} and "
                        f" --COUNTRY="
                        f"{arguments.country}, please "
                        f"enter wifi password:"******""
            if 'ubuntu' not in tag:
                Console.error(
                    "This command only supports burning ubuntu cards")
                return ""
            sdcard = SDCard(card_os="ubuntu")

            # Code below taken from arguments.sdcard
            try:
                USB.check_for_readers()
            except Exception as e:
                print()
                Console.error(e)
                print()
                return ""

            # determine if we are burning a manager, as this needs to be done
            # first to get the ssh public key
            # manager = False
            # for name in names:
            #     if not inv.has_host(name):
            #         Console.error(f'Could not find {name} in inventory {inv.filename}')
            #         return ""
            #     service = inv.get(name=name, attribute='service')
            #     if service == 'manager' and not manager:
            #         manager = name
            #         # make manager first in names
            #         names.remove(name)
            #         names.insert(0, name)
            #     elif service == 'manager' and manager:
            #         raise Exception('More than one manager detected in NAMES')

            for name in names:
                if not yn_choice(
                        f'Is the card to be burned for {name} inserted?'):
                    if not yn_choice(
                            f"Please insert the card to be burned for {name}. "
                            "Type 'y' when done or 'n' to terminante"):
                        Console.error("Terminating: User Break")
                        return ""

                service = inv.get(name=name, attribute='service')
                # Make sure bridge is only enabled if WiFi enabled
                if service == 'manager':
                    services = inv.get(name=name, attribute='services')
                    if 'bridge' in services and not arguments.ssid:
                        Console.error(
                            'Service bridge can only be configured if WiFi'
                            ' is enabled with --ssid and --wifipassword')
                        return ""
                    else:
                        enable_bridge = 'bridge' in services

                Console.info(f'Burning {name}')
                sdcard.format_device(device=arguments.device, yes=True)
                sdcard.unmount(device=arguments.device)
                sdcard.burn_sdcard(tag=tag, device=arguments.device, yes=True)
                sdcard.mount(device=arguments.device, card_os="ubuntu")
                if service == 'manager':
                    # Generate a private public key pair for the manager that will be persistently used
                    # priv_key, pub_key = c.generate_ssh_key(name)
                    # Write priv_key and pub_key to /boot/id_rsa and /boot/id_rsa.pub
                    # SDCard.writefile(filename=f'{sdcard.boot_volume}/id_rsa', content=priv_key)
                    # SDCard.writefile(filename=f'{sdcard.boot_volume}/id_rsa.pub', content=pub_key)
                    c.build_user_data(
                        name=name,
                        country=arguments.country,
                        upgrade=arguments.upgrade,
                        with_bridge=enable_bridge).write(
                            filename=sdcard.boot_volume + '/user-data')
                    c.build_network_data(name=name, ssid=arguments.ssid, password=arguments.wifipassword)\
                        .write(filename=sdcard.boot_volume + '/network-config')
                else:
                    c.build_user_data(
                        name=name,
                        add_manager_key=manager,
                        upgrade=arguments.upgrade).write(
                            filename=sdcard.boot_volume + '/user-data')
                    c.build_network_data(name=name).write(
                        filename=sdcard.boot_volume + '/network-config')
                time.sleep(
                    1
                )  # Sleep for 1 seconds to give ample time for writing to finish
                sdcard.unmount(device=arguments.device, card_os="ubuntu")

                Console.info("Remove card")

            Console.ok(f"Burned {len(names)} card(s)")
            return ""

        elif arguments.firmware and arguments.check:

            execute("firmware check", burner.firmware(action="check"))
            return ""

        elif arguments.firmware and arguments.update:

            execute("firmware update", burner.firmware(action="update"))
            return ""

        if arguments.check:

            execute("check", burner.check(device=arguments.device))
            return ""

        elif arguments.versions and arguments['image']:

            StopWatch.start("image versions")

            result = Image.create_version_cache(refresh=arguments["--refresh"])

            output = "table"
            if arguments["--yaml"]:
                output = "yaml"

            order = ["tag", 'date', "os", "type", 'version']
            header = ["Tag", 'Date', "OS", "Type", 'Version']
            if arguments.details:
                order = ["tag", 'date', "os", "type", 'version', "url"]
                header = ["Tag", 'Date', "OS", "Type", 'Version', "Url"]

            print(
                Printer.write(result,
                              order=order,
                              header=header,
                              output=output))

            StopWatch.stop("image versions")
            StopWatch.status("image versions", True)
            return ""

        elif arguments.load:
            execute("load", sdcard.load_device(device=arguments.device))
            return ""

        elif arguments[
                "format"]:  # as format is a python word, we need to use an index

            execute(
                "format",
                sdcard.format_device(device=arguments.device, unmount=True))
            return ""

        elif arguments.network and arguments["list"]:

            if os_is_mac():
                Console.error("Not yet implemented on MacOS")
                return ""

            ip = arguments.ip or Network.address()[0]['local']

            details = Network.nmap(ip=ip)

            if arguments.used:

                print(','.join([x['ip'] for x in details]))

            else:
                print(
                    Printer.write(details,
                                  order=[
                                      'name',
                                      "ip",
                                      "status",
                                      "latency",
                                  ],
                                  header=[
                                      'Name',
                                      "IP",
                                      "Status",
                                      "Latency",
                                  ]))
            return ""

        elif arguments.network:

            if os_is_mac():
                Console.error("Not yet implemented on MacOS")
                return ""

            # print (Network.nmap())
            details = Network.address()

            print(
                Printer.write(details,
                              order=['label', "local", "broadcast"],
                              header=["Label", "Local", "Broadcast"]))
            return ""

        elif arguments.wifi:

            password = arguments.passwd
            ssid = arguments.ssid or get_ssid()
            country = arguments.country

            if password is None:
                password = getpass("Please enter the Wifi password or enter "
                                   "for no password: "******"macos"
            elif os_is_linux():
                host = "linux"
            elif os_is_pi():
                host = "raspberry"
            else:
                Console.error(
                    "This command is not yet implemented for your OS")
                return ""

            burner.configure_wifi(ssid,
                                  psk=password,
                                  country=country,
                                  host=host)

            return ""

        elif arguments.info:

            output = arguments.output or "table"
            card = SDCard()
            execute("info", card.info(output=output))
            try:
                USB.check_for_readers()
            except Exception as e:
                print()
                Console.error(e)
                print()
                return ""

            return ""

        elif arguments.install:

            if os_is_mac():
                Console.error("Not yet implemented on MacOS")
                return ""

            execute("install", burner.install())
            return ""

        elif arguments.shrink:

            if os_is_mac():
                Console.error("Not yet implemented on MacOS")
                return ""

            execute("shrink", burner.shrink(image=arguments.IMAGE))
            return ""

        elif arguments.backup:
            try:
                USB.check_for_readers()
            except Exception as e:
                print()
                Console.error(e)
                print()
                return ""
            execute(
                "backup",
                sdcard.backup(device=arguments.device, to_file=arguments.to))
            return ""

        elif arguments[
                "copy"]:  # as copy is a reserved word we need to use the index
            USB.check_for_readers()
            execute(
                "copy",
                sdcard.copy(device=arguments.device, from_file=arguments.FROM))
            return ""

        elif arguments.sdcard:

            try:
                USB.check_for_readers()
            except Exception as e:
                print()
                Console.error(e)
                print()
                return ""

            if arguments.device is None:
                card = SDCard()
                card.info()
                Console.error("Please specify a device")
                return ""

            arguments.TAG = arguments.TAG or ["latest-lite"]
            if any("ubuntu" in tag for tag in arguments.TAG):
                sdcard = SDCard(card_os="ubuntu")

            execute(
                "format",
                sdcard.format_device(device=arguments.device, unmount=True))
            execute("unmount", sdcard.unmount(device=arguments.device))

            execute(
                "sdcard",
                sdcard.burn_sdcard(tag=arguments.TAG,
                                   device=arguments.device,
                                   yes=arguments.yes))
            return ""

        elif arguments.mount:

            if arguments.device is None:
                card = SDCard
                card.info()
                Console.error("Please specify a device")
                return ""

            execute(
                "mount",
                sdcard.mount(device=arguments.device, card_os=arguments.os))
            return ""

        elif arguments.unmount:

            card = SDCard(card_os=arguments.os)
            execute(
                "unmount",
                card.unmount(device=arguments.device, card_os=arguments.os))
            return ""

        elif arguments.mac:

            hostnames = Parameter.expand(arguments.hostname)

            execute("mac", burner.mac(hostnames=hostnames))
            return ""

        elif arguments.set:

            try:
                USB.check_for_readers()
            except Exception as e:
                print()
                Console.error(e)
                print()
                return ""

            if arguments.hostname:
                execute("set hostname",
                        burner.set_hostname(arguments.hostname))

            if arguments.ip:
                execute("set ip", burner.set_static_ip(arguments.ip))

            if arguments.key:
                execute("set key", burner.set_key(arguments.key))

            if arguments.keyboard:
                execute("set keyboard",
                        burner.keyboard(country=arguments.keyboard))

            if arguments.cmdline:
                execute("set cmdline", burner.set_cmdline(arguments.cmdline))

            return ""

        elif arguments.enable and arguments.ssh:
            try:
                USB.check_for_readers()
            except Exception as e:
                print()
                Console.error(e)
                print()
                return ""

            execute("enable ssh", burner.enable_ssh())
            return ""

        # elif arguments.versions and arguments.image:
        #    image = Image()

        elif arguments.ls and arguments['image']:
            execute("image ls", Image().ls())
            return ""

        elif arguments.delete and arguments.IMAGE:
            execute("image rm", Image().rm(arguments.IMAGE))
            return ""

        elif arguments["get"] and arguments['image'] and arguments["--url"]:
            image = Image()
            execute("image fetch", image.fetch(url=arguments.url))
            return ""

        elif arguments["get"] and arguments['image'] and arguments["TAG"]:

            tag = arguments["TAG"]
            if "latest" in tag and ("full" in tag or "lite" in tag):
                result = Image.create_version_cache(
                    refresh=arguments["--refresh"])

            image = Image()
            execute("image fetch", image.fetch(tag=arguments["TAG"]))
            return ""

        elif arguments["get"] and arguments['image']:
            image = Image()
            execute("image fetch", image.fetch(tag="latest"))
            return ""

        elif arguments.cluster:

            # is true when
            #
            # cms burn cluster --hostname=red,red00[1-2]
            #                  --device=/dev/sdb
            #                  --ip=10.1.1.[1-3]
            #                  --ssid=myssid
            #                  --wifipassword=mypass
            #

            try:
                USB.check_for_readers()
            except Exception as e:
                print()
                Console.error(e)
                print()
                return ""
            execute("cluster", burner.cluster(arguments=arguments))
            return ""

        elif arguments.create and arguments.inventory:
            try:
                USB.check_for_readers()
            except Exception as e:
                print()
                Console.error(e)
                print()
                return ""

            if not os_is_pi():
                print()
                Console.error(
                    "This command has only been written for a  Raspberry Pis. "
                    "Terminating for caution")
                print()
                if yn_choice("Continue anyways?"):
                    pass
                else:
                    return
            if not arguments.name:
                Console.error(
                    "Missing --name parameter. See cms help burn for usage")
                return ""
            if not arguments.device:
                Console.error(
                    "Missing --device parameter. See cms help burn for usage")
                return ""

            StopWatch.start("burn inventory")
            multi_burner = MultiBurner()
            # Perhaps we want to change the path at some point
            inventory = f"~/.cloudmesh/{arguments.inventory}"
            multi_burner.burn_inventory(inventory=inventory,
                                        name=arguments.name,
                                        device=arguments.device,
                                        yes=arguments.yes,
                                        passwd=arguments.passwd)
            StopWatch.stop("burn inventory")
            StopWatch.status("burn inventory", True)
            StopWatch.benchmark(sysinfo=False, csv=False)
            return ""

        elif arguments.create:

            try:
                USB.check_for_readers()
            except Exception as e:
                print()
                Console.error(e)
                print()
                return ""

            if arguments["--passwd"]:
                passwd = arguments["--passwd"]
            elif "PASSWD" in os.environ:
                passwd = os.environ["PASSWD"]
            else:
                passwd = generate_strong_pass()

            psk = None
            if arguments["--ssid"]:
                ssid = arguments["--ssid"]
                if arguments["--wifipassword"]:
                    psk = arguments["--wifipassword"]
                else:
                    psk = None
            else:
                if arguments["--wifipassword"]:
                    print("Can't have wifi password with no ssid")
                    return
                else:
                    ssid = None

            image = 'latest' or arguments.IMAGE

            dev = os.environ['DEV'] if 'DEV' in os.environ else None
            devices = arguments["--device"] or dev or None

            if devices is not None:
                devices = Parameter.expand_string(devices)

            hostnames = Parameter.expand(arguments.hostname)

            if arguments.burnimg is None:
                burning = hostnames
            else:
                burning = arguments.burning

            VERBOSE(arguments)

            ips = None if not arguments.ip else Parameter.expand(arguments.ip)
            key = arguments.sshkey
            tag = arguments['--tag']

            if os_is_pi() or os_is_linux():
                blocksize = arguments.blocksize
                StopWatch.start("total")

                multi = MultiBurner()
                multi.burn_all(
                    burning=burning,
                    image=image,
                    device=devices,
                    blocksize=blocksize,
                    progress=True,
                    hostnames=hostnames,
                    # not difference between names and name, maybe we should align
                    ips=ips,
                    key=key,
                    password=passwd,
                    ssid=ssid,
                    psk=psk,
                    tag=tag,
                    yes=arguments.yes)

                StopWatch.stop("total")
                StopWatch.status("total", True)

                StopWatch.benchmark(sysinfo=False, csv=False)
            else:
                Console.error(
                    "This command is only supported ona Pi and Linux")
            return ""

        Console.error("see manual page: cms help burn")
        return ""
Ejemplo n.º 18
0
    def run(self,
            script=None,
            hosts=None,
            username=None,
            processors=4,
            verbose=False):

        results = []

        if type(hosts) != list:
            hosts = Parameter.expand(hosts)

        hostname = os.uname()[1]
        for command in script.splitlines():
            if hosts == None:
                hosts = ""
            if command.startswith("#") or command.strip() == "":
                print(command)
            elif len(hosts) == 1 and hosts[0] == self.hostname:
                host = hosts[0]
                command = command.format(user=self.user,
                                         version=self.version,
                                         host=host,
                                         hostname=hostname)
                print(hosts, "->", command)
                if self.dryrun:
                    Console.info(f"Executiong command >{command}<")
                else:
                    os.system(command)
            elif len(hosts) == 1 and hosts[0] != self.hostname:
                host = hosts[0]
                command = command.format(user=self.user,
                                         version=self.version,
                                         host=host,
                                         hostname=hostname)
                print(hosts, "->", command, hosts)
                if self.dryrun:
                    Console.info(f"Executiong command >{command}<")
                else:
                    os.system(f"ssh {host} {command}")
            else:

                #@staticmethod
                #def run(hosts=None,
                #        command=None,
                #        execute=None,
                #        processors=3,
                #        shell=False,
                #        **kwargs):

                if self.dryrun:
                    executor = print
                else:
                    executor = os.system

                result = Host.ssh(
                    hosts=hosts,
                    command=command,
                    username=username,
                    key="~/.ssh/id_rsa.pub",
                    processors=processors,
                    executor=executor,
                    version=self.
                    version,  # this was your bug, you did not pass this along
                    user=self.
                    user  # this was your bug, you did not pass this along
                )
                results.append(result)
        if verbose:
            pprint(results)
            for result in results:
                print(Printer.write(result, order=['host', 'stdout']))
        return results
Ejemplo n.º 19
0
    def do_host(self, args, arguments):
        """
        ::

          Usage:
              host scp NAMES SOURCE DESTINATION [--dryrun]
              host ssh NAMES COMMAND [--dryrun] [--output=FORMAT]
              host config NAMES [IPS] [--user=USER] [--key=PUBLIC]
              host check NAMES [--user=USER] [--key=PUBLIC]
              host key create NAMES [--user=USER] [--dryrun] [--output=FORMAT]
              host key list NAMES [--output=FORMAT]
              host key gather NAMES [--authorized_keys] [FILE]
              host key scatter USERNAME NAMES FILE

          This command does some useful things.

          Arguments:
              FILE   a file name

          Options:
              --dryrun   shows what would be done but does not execute
              --output=FORMAT  the format of the output

          Description:

              host scp NAMES SOURCE DESTINATION

                TBD

              host ssh NAMES COMMAND

                runs the command on all specified hosts
                Example:
                     ssh red[01-10] \"uname -a\"

              host key create NAMES
                create a ~/.ssh/id_rsa and id_rsa.pub on all hosts specified
                Example:
                    ssh key create "red[01-10]"

              host key list NAMES

                list all id_rsa.pub keys from all hosts specifed
                 Example:
                     ssh key list red[01-10]

              host key gather HOSTS FILE

                gathers all keys from file FILE including the one from localhost.

                    ssh key gather "red[01-10]" keys.txt

              host key scatter USERNAME HOSTS FILE

                copies all keys from file FILE to authorized_keys on all hosts,
                but also makes sure that the users ~/.ssh/id_rsa.pub key is in
                the file.

                1) adds ~/.id_rsa.pub to the FILE only if its not already in it
                2) removes all duplicated keys

                Example:
                    ssh key scatter pi "red[01-10]" ~/keys.txt

              host key scp NAMES FILE

                copies all keys from file FILE to authorized_keys on all hosts
                but also makes sure that the users ~/.ssh/id_rsa.pub key is in
                the file and removes duplicates, e.g. it calls fix before upload

                Example:
                    ssh key list red[01-10] > pubkeys.txt
                    ssh key scp red[01-10] pubkeys.txt

              host config NAMES IPS [--user=USER] [--key=PUBLIC]

                generates an ssh config file tempalte that can be added to your
                .ssh/config file

                Example:
                    cms host config "red,red[01-03]" "198.168.1.[1-4]" --user=pi

              host check NAMES [--user=USER] [--key=PUBLIC]

                This command is used to test if you can login to the specified
                hosts. It executes the hostname command and compares it.
                It provides a table  with a sucess column

                cms host check "red,red[01-03]"

                    +-------+---------+--------+
                    | host  | success | stdout |
                    +-------+---------+--------+
                    | red   | True    | red    |
                    | red01 | True    | red01  |
                    | red02 | True    | red02  |
                    | red03 | True    | red03  |
                    +-------+---------+--------+

        """
        def _print(results):
            arguments.output = arguments.output or 'table'

            if arguments.output == 'table':
                print(
                    Printer.write(results, order=['host', 'success',
                                                  'stdout']))
            else:
                pprint(results)

        map_parameters(arguments, 'dryrun', 'output', 'user')
        dryrun = arguments.dryrun

        if dryrun:
            VERBOSE(arguments)

        if arguments.scp and not arguments.key:

            destinations = Parameter.expand(arguments.DESTINATION)
            source = arguments.SOURCE
            results_key = Host.scp(source, destinations, output="lines")

        elif arguments.ssh:
            names = Parameter.expand(arguments.NAMES)

            # print (names)

            results = Host.ssh(hosts=names, command=arguments.COMMAND)
            _print(results)

        elif arguments.key and arguments.create:

            Host.ssh_keygen(hosts=arguments.NAMES,
                            username=arguments.user,
                            dryrun=dryrun)

        #_print(results)

        elif arguments.key and arguments.list:

            names = Parameter.expand(arguments.NAMES)
            jobSet = JobSet("key_list", executor=JobSet.ssh)
            command = "cat .ssh/id_rsa.pub"
            for host in names:
                jobSet.add({"name": host, "host": host, "command": command})
            jobSet.run(parallel=len(names))

            #results = Host.ssh(hosts=names,
            #                   command='cat .ssh/id_rsa.pub',
            #                   username=arguments.user)

            jobSet.Print()

        elif arguments.key and arguments.gather:

            output = Host.gather_keys(username=arguments.user,
                                      hosts=arguments.NAMES,
                                      filename="~/.ssh/id_rsa.pub",
                                      key="~/.ssh/id_rsa",
                                      processors=3,
                                      dryrun=False)

            if arguments.FILE:
                filename = path_expand(arguments.FILE)
                directory = os.path.dirname(filename)
                if directory:
                    Shell.mkdir(directory)
                with open(filename, "w") as f:
                    f.write(output)
            else:
                print(output)

        elif arguments.key and arguments.scatter:

            names = arguments.NAMES
            file = arguments.get("FILE")

            if not os.path.isfile(file):
                Console.error("The file does not exist")
                return ""

            Host.put(hosts=names,
                     source=file,
                     username=arguments.USERNAME,
                     destination=".ssh/authorized_keys")

            #_print(result)

        elif arguments.config:

            key = arguments.key or "~/.ssh/id_rsa.pub"
            result = Host.config(hosts=arguments.NAMES,
                                 ips=arguments.IPS,
                                 username=arguments.user,
                                 key=key)
            print(result)

        elif arguments.check:

            key = arguments.key or "~/.ssh/id_rsa.pub"
            result = Host.check(hosts=arguments.NAMES,
                                username=arguments.user,
                                key=key)
            for entry in result:
                entry['success'] = entry['stdout'] == entry['host']

            _print(result)

        return ""
Ejemplo n.º 20
0
    def do_inventory(self, args, arguments):
        """
        ::

          Usage:
              inventory add cluster NAMES
              inventory add NAMES [--label=LABEL]
                                  [--services=SERVICES]
                                  [--project=PROJECT]
                                  [--owners=OWNERS]
                                  [--comment=COMMENT]
                                  [--inventory=INVENTORY]
                                  [--cluster=CLUSTER]
                                  [--ip=IP]
                                  [--service=SERVICE]
                                  [--tag=TAG]
                                  [--keyfile=KEYFILE]
                                  [--router=ROUTER]
                                  [--locale=LOCALE]
                                  [--timezone=TIMEZONE]
              inventory create TAG [--hostnames=NAMES]
                                   [--ip=IP]
                                   [--inventory=INVENTORY]
                                   [--keyfile=KEYFILE]
              inventory set NAMES ATTRIBUTE to VALUES [--inventory=INVENTORY] [--listvalue]
              inventory delete NAMES [--inventory=INVENTORY]
              inventory clone NAMES from SOURCE [--inventory=INVENTORY]
              inventory list [NAMES] [--format=FORMAT] [--columns=COLUMNS] [--inventory=INVENTORY]
              inventory info [--inventory=INVENTORY]
              inventory remove --inventory=INVENTORY

          Arguments:
            NAMES     Name of the resources (example i[10-20])
            FORMAT    The format of the output is either txt,
                      yaml, dict, table [default: table].
            OWNERS    a comma separated list of owners for this resource
            LABEL     a unique label for this resource
            SERVICE   a string that identifies the service
            PROJECT   a string that identifies the project
            SOURCE    a single host name to clone from
            COMMENT   a comment

          Options:
             -v       verbose mode
             --keyfile=KEYFILE      Keyfile to assign [default: ~/.ssh/id_rsa.pub]

          Description:

                add -- adds a resource to the resource inventory
                list -- lists the resources in the given format
                delete -- deletes objects from the table
                clone -- copies the content of an existing object
                         and creates new once with it
                set   -- sets for the specified objects the attribute
                         to the given value or values. If multiple values
                         are used the values are assigned to the and
                         objects in order. See examples
                map   -- allows to set attributes on a set of objects
                         with a set of values

          Examples:

            cms inventory add x[0-3] --service=openstack
                adds hosts x0, x1, x2, x3 and puts the string
                openstack into the service column

            cms inventory list
                lists the repository

            cms inventory set x[3-4] temperature to 32
                sets for the resources x3, x4 the value of the
                temperature to 32

            cms inventory set x[7-8] ip to 128.0.0.[0-1]
                sets the value of x7 to 128.0.0.0
                sets the value of x8 to 128.0.0.1

            cms inventory set x1 services to bridge,kubernetes --listvalue
                sets the value of x1 to [bridge, kubernetes]
                The --listvalue option indicates the value set is a list

            cms inventory clone x[5-6] from x3
                clones the values for x5, x6 from x3

        """
        map_parameters(arguments, "columns", 'ip', 'hostnames', 'inventory',
                       'keyfile', 'listvalue')

        if arguments.info:

            if arguments.inventory is None:
                i = Inventory()
            else:
                i = Inventory(f'~/.cloudmesh/{arguments.inventory}')
            i.read()
            i.info()

        elif arguments.remove and arguments.inventory:

            os.system("rm -f " +
                      path_expand(f'~/.cloudmesh/{arguments.inventory}'))

        elif arguments.NAMES is not None and arguments.list:

            hosts = Parameter.expand(arguments.NAMES)

            if arguments.inventory is None:
                i = Inventory()
            else:
                i = Inventory(f'~/.cloudmesh/{arguments.inventory}')
            i.read()
            d = dict(i.data)
            r = {}
            for key in d:
                if key in hosts:
                    r[key] = d[key]

            pprint(r)
            i.data = r

            if arguments["--columns"]:
                order = arguments["--columns"].split(",")
            else:
                order = i.order
            print(i.list(format="table", order=order))

        # elif arguments["set"]:
        #    hosts = hostlist.expand_hostlist(arguments.NAMES)
        #    i = inventory()
        #    i.read()
        #    element = {}

        #    for attribute in i.order:
        #        try:
        #            attribute = arguments["ATTRIBUTE"]
        #            value = arguments["VALUE"]
        #            if value is not None:
        #                element[attribute] = value
        #        except:
        #            pass
        #    element['host'] = arguments.NAMES
        #    i.add(**element)
        #    print (i.list(format="table"))

        elif arguments.create:
            tag = arguments.TAG

            hostnames = Parameter.expand(arguments.hostnames)
            manager, workers = Host.get_hostnames(hostnames)

            ips = Parameter.expand(arguments.ip)

            if len(ips) != len(hostnames):
                Console.error(
                    "The number of hosts does not match the number of ips")
                return

            keyfile = arguments.keyfile or '~/.ssh/id_rsa.pub'

            if arguments.inventory is None:
                arguments.inventory = "inventory.yaml"

            if manager is None:
                manager_ip = None
                worker_ips = ips
            else:
                manager_ip = ips[0]
                worker_ips = ips[1:]

            worker_hostnames = workers

            if arguments.inventory is None:
                i = Inventory()
            else:
                i = Inventory(f'~/.cloudmesh/{arguments.inventory}')
            i.read()

            inventory_name = arguments.inventory.split('.')[0]

            i.add(host=manager,
                  name=manager,
                  tag=tag,
                  cluster=inventory_name,
                  service='manager',
                  ip=manager_ip,
                  keyfile=keyfile,
                  status="inactive")

            for worker, ip in zip(worker_hostnames, worker_ips):
                i.add(host=worker,
                      name=worker,
                      tag=tag,
                      cluster=inventory_name,
                      service='worker',
                      ip=ip,
                      keyfile=keyfile,
                      status="inactive")  # noqa: E124

            i.save()
            Console.ok(
                f"Successfuly saved to ~/.cloudmesh/{arguments.inventory}")

        elif arguments.list:

            if arguments.inventory is None:
                i = Inventory()
            else:
                i = Inventory(f'~/.cloudmesh/{arguments.inventory}')
            i.read()
            if arguments["--columns"]:
                order = arguments["--columns"].split(",")
            else:
                order = i.order
            print(i.list(format="table", order=order))

        elif arguments.set:

            hosts = Parameter.expand(arguments.NAMES)
            values = Parameter.expand(arguments.VALUES)
            if len(values) == 1:
                values = values * len(hosts)
            attribute = arguments.ATTRIBUTE
            if not arguments.listvalue and len(hosts) != len(values):
                Console.error(
                    "Number of names {:} != number of values{:}".format(
                        len(hosts), len(values)))
            if arguments.inventory is None:
                i = Inventory()
            else:
                i = Inventory(f'~/.cloudmesh/{arguments.inventory}')
            i.read()

            for index in range(0, len(hosts)):
                host = hosts[index]
                value = values if arguments.listvalue else values[index]

                if not i.has_host(host):
                    i.add(host=host)

                i.set(host, attribute, value)
                # object = {'host': host,
                #           attribute: value}

                # i.add(**object)
                i.save()

            print(i.list(format="table"))

        elif arguments.add and arguments.cluster:
            names = Parameter.expand(arguments.NAMES)
            manager, workers = Host.get_hostnames(names)
            if manager is None:
                Console.error(
                    "A manager node is required. Numbers are not allowed in a manger name, "
                    "i.e. red is an acceptable manager name, red00 is not.")
                return
            if workers:
                worker_base_name = ''.join(
                    [i for i in workers[0] if not i.isdigit()])
            cluster_name = manager or worker_base_name
            inventory = path_expand(
                f'~/.cloudmesh/inventory-{cluster_name}.yaml')
            Inventory.build_default_inventory(filename=inventory,
                                              manager=manager,
                                              workers=workers)

        elif arguments.add:

            hosts = Parameter.expand(arguments.NAMES)
            if arguments.inventory is None:
                i = Inventory()
            else:
                i = Inventory(f'~/.cloudmesh/{arguments.inventory}')
            i.read()
            element = {}

            for attribute in i.order:
                try:
                    value = arguments["--" + attribute]
                    if value is not None:
                        element[attribute] = value
                except Exception as e:  # noqa: F841
                    pass
            element['host'] = arguments.NAMES
            element['status'] = 'inactive'
            i.add(**element)
            i.save()
            print(i.list(format="table"))

        elif arguments.delete:

            hosts = Parameter.expand(arguments.NAMES)
            if arguments.inventory is None:
                i = Inventory()
            else:
                i = Inventory(f'~/.cloudmesh/{arguments.inventory}')
            i.read()

            for host in hosts:
                i.delete(host)
            i.save()

        elif arguments.clone:

            hosts = Parameter.expand(arguments.NAMES)
            source = arguments.SOURCE

            if arguments.inventory is None:
                i = Inventory()
            else:
                i = Inventory(f'~/.cloudmesh/{arguments.inventory}')
            i.read()

            if source in i.data:

                for host in hosts:
                    i.data[host] = dict(i.data[source])
                i.save()
            else:
                Console.error("The source {:} does not exist".format(source))

        return ""
Ejemplo n.º 21
0
    def run(self):

        Sudo.password()

        host = None
        ips = None
        hostnames = None
        key = None
        event = None
        tags = None
        kind = None
        device = None

        while True:

            event, values = self.window.read()

            if event in ("Cancel", 'cancel', None):
                if not self.no_diagram:
                    rack_file = f"~/.cloudmesh/gui/{self.manager}-rack.png"
                    net_file = f"~/.cloudmesh/gui/{self.manager}-net.png"
                    os.remove(path_expand(rack_file))
                    os.remove(path_expand(net_file))
                break

            #
            # UPDATE OS SELECTION
            #
            if event.startswith("os"):
                for image in values:
                    if image.startswith("os") and values[image]:
                        break

                self.logger(f"Switch OS to: {image}")

                image_manager = image_tags[image]["manager"]
                image_worker = image_tags[image]["worker"]

                self.window[f'tags-{self.manager}'].update(image_manager)
                for worker in self.workers:
                    self.window[f'tags-{worker}'].update(image_worker)
                self.window.Refresh()

            if event.startswith("button"):

                #
                # set imaged string
                #
                imaged = values['imaged']
                if not imaged:
                    self.imaged_str = "--imaged"
                else:
                    self.imaged_str = ""
                #
                # handle device, hostnames and ips
                #
                ips = []
                hostnames = []
                tags = []
                for entry in values:
                    if str(entry).startswith("name"):
                        hostnames.append(values[entry])
                    if str(entry).startswith("ip"):
                        ips.append(values[entry])
                    if str(entry).startswith("device-") and values[entry]:
                        device = "/dev/" + entry.replace("device-", "")
                    if str(entry).startswith("tag") and values[entry]:
                        tags.append(values[entry])

                key = values['key']
                self.hostnames_str = ','.join(hostnames)
                self.ips_str = ','.join(ips)
                #
                # get the ssid
                #
                self.ssid = values['ssid']
                self.wifipassword = values['wifi']

                host = event.replace("button-", "")
                self.set_button_color(host, 'grey')
                if host == self.manager:
                    kind = "manager"
                else:
                    kind = "worker"

                print()
                print("Host:    ", host)
                print("IPs:     ", ips)
                print("Hostnames", hostnames)
                print("Ssid:    ", self.ssid)
                print("Key:     ", key)
                print("Event:   ", event)
                print("Tags:    ", tags)
                print("Kind:    ", kind)
                print("Device:  ", device)
                print("Format   ", imaged)
                print()

                # Call burn function for manager and workers
                self.logger(f"Burning {kind} {host}")
                # tags = values[f'tags-{host}']
                self.window[f'status-{host}'].update(' Burning ')

                if not self.no_diagram:
                    self.update_diagram_colors(self.manager, host, "blue")
                self.hostnames_str = ','.join(hostnames)
                self.ips_str = ','.join(ips)
                # command = f"cms burn cluster --device={device}" \
                #          f" --hostname={self.hostnames_str}" \
                #          f" --ssid={self.ssid}" \
                #          f" --wifipassword={self.wifipassword}" \
                #          f" --ip={self.ips_str}" \
                #          f" --burning={host}" \
                #          " -y" \
                #          f" {self.imaged_str}"

                manager, workers = Host.get_hostnames(hostnames)
                filename = path_expand(f"~/.cloudmesh/inventory-{manager}.yaml")
                Inventory.build_default_inventory(filename=filename, manager=manager,
                                                  workers=workers, ips=ips,
                                                  gui_images=tags)

                if "ubuntu" in tags[0]:
                    os_cmd = 'ubuntu'
                else:
                    os_cmd = 'raspberry'

                if host == manager:
                    command = f"cms burn {os_cmd} {host}" \
                              f" --device={device}" \
                              f" --ssid={self.ssid}" \
                              f" --wifipassword={self.wifipassword}" \
                              f" --country={Shell.locale().upper()}"
                else:
                    command = f"cms burn {os_cmd} {host}" \
                              f" --device={device}"
                    print(command)

                try:
                    self.logger(f"Executing: {command}")
                    if self.dryrun:
                        time.sleep(0.5)
                    else:
                        os.system(command)
                    self.window[f'status-{host}'].update(' Completed ')
                    if not self.no_diagram:
                        self.update_diagram_colors(self.manager, host, "green")
                    self.set_button_color(host, 'green')

                except Exception as e:
                    print(e)
                    self.logger("Command failed")
                    if not self.no_diagram:
                        self.update_diagram_colors(self.manager, host, "orange")
                    self.set_button_color(host, 'red')

                self.window.FindElement(f'button-{host}').Update(button_color=('white', 'green'))

                self.window.Refresh()

        print('exit')
        self.window.close()
Ejemplo n.º 22
0
    def do_host(self, args, arguments):
        """
        ::

          Usage:
              host scp NAMES SOURCE DESTINATION [--dryrun]
              host ssh NAMES COMMAND [--dryrun] [--output=FORMAT]
              host config NAMES --ips=IPS [--user=USER] [--key=PUBLIC]
              host config --proxy=PROXY NAMES [--user=USER] [--append] [--local=no] [--StrictHostKeyChecking=no] [--cluster=name]
              host config NAMES [--user=USER] [--append] [--local=no] [--StrictHostKeyChecking=no] [--cluster=name]
              host find [NAMES] [--user=USER] [--table|--json] [--verbose]
              host check NAMES [--user=USER] [--key=PUBLIC]
              host key create NAMES [--user=USER] [--dryrun] [--output=FORMAT]
              host key list NAMES [--output=FORMAT]
              host key setup NAMES
              host key gather NAMES [--authorized_keys] [FILE]
              host key scatter NAMES [FILE] [--user=USER]
              host key add NAMES [FILE]
              host key delete NAMES [FILE]
              host key access NAMES [FILE] [--user=USER]
              host tunnel create NAMES [--port=PORT]
              host mac NAMES [--eth] [--wlan] [--output=FORMAT]
              host setup WORKERS [LAPTOP]
              host shutdown NAMES
              host reboot NAMES
              host adduser NAMES USER
              host passwd NAMES USER
              host addsudo NAMES USER
              host deluser NAMES USER
              host ping NAMES
              host info NAMES


          This command does some useful things.

          Arguments:
              FILE   a file name

          Options:
              --dryrun         shows what would be done but does not execute
              --output=FORMAT  the format of the output
              --port=PORT      starting local port for tunnel assignment
              --local=no       do not append .local to manager hostname [default: yes]
              --user=USER      username for manager and workers [default: pi]
              --ips=IPS        ip addresses of the manager and workers
              --StrictHostKeyChecking=no  if set to yes, strict host checking is enforced [default: no]
              --ProxyJump=no  if set to yes, a proxyjump is performed for each worker through the manager [default: yes]

          Description:

              host scp NAMES SOURCE DESTINATION

                Uses scp to transfer Source to NAMES:DESTINATION.

              host ssh NAMES COMMAND

                runs the command on all specified hosts
                Example:
                     ssh red[01-10] \"uname -a\"

              host key create NAMES
                create a ~/.ssh/id_rsa and id_rsa.pub on all hosts specified
                Example:
                    ssh key create "red[01-10]"

              host key list NAMES

                list all id_rsa.pub keys from all hosts specifed
                 Example:
                     ssh key list red[01-10]

              host key gather HOSTS FILE

                gathers all keys from file FILE including the one from localhost.

                    ssh key gather "red[01-10]" keys.txt

              host key scatter HOSTS FILE [--user=USER]

                copies all keys from file FILE to authorized_keys on all hosts,
                but also makes sure that the users ~/.ssh/id_rsa.pub key is in
                the file. If provided the optional user, it will add the keys to
                that user's .ssh directory. This is often required when
                adding a new user in which case HOSTS should still a sudo
                user with ssh currently enabled.

                1) adds ~/.id_rsa.pub to the FILE only if its not already in it
                2) removes all duplicated keys

                Example:
                    ssh key scatter "red[01-10]"
                    ssh key scatter pi@red[01-10] keys.txt --user=alice

              host key add NAMES FILE

                Adds all keys in FILE into the authorized_keys of NAMES.

                Example:
                    cms host key add worker001 ~/.ssh/id_rsa.pub

              host key delete NAMES FILE

                Deletes all keys in fILE from authorized_keys of NAMES if they exist.

                Example
                    cms host key delete worker001 ~/.ssh/id_rsa.pub

              host key scp NAMES FILE

                copies all keys from file FILE to authorized_keys on all hosts
                but also makes sure that the users ~/.ssh/id_rsa.pub key is in
                the file and removes duplicates, e.g. it calls fix before upload

                Example:
                    ssh key list red[01-10] > pubkeys.txt
                    ssh key scp red[01-10] pubkeys.txt

              host config NAMES IPS [--user=USER] [--key=PUBLIC]

                generates an ssh config file tempalte that can be added to your
                .ssh/config file

                Example:
                    cms host config "red,red[01-03]" "198.168.1.[1-4]" --user=pi

              host check NAMES [--user=USER] [--key=PUBLIC]

                This command is used to test if you can login to the specified
                hosts. It executes the hostname command and compares it.
                It provides a table  with a sucess column

                cms host check "red,red[01-03]"

                    +-------+---------+--------+
                    | host  | success | stdout |
                    +-------+---------+--------+
                    | red   | True    | red    |
                    | red01 | True    | red01  |
                    | red02 | True    | red02  |
                    | red03 | True    | red03  |
                    +-------+---------+--------+

              host tunnel create NAMES [--port=PORT]

                This command is used to create a persistent local port
                forward on the host to permit ssh tunnelling from the wlan to
                the physical network (eth). This registers an autossh service in
                systemd with the defualt port starting at 8001.

                Example:
                    cms host tunnel create red00[1-3]

              host mac NAMES

                returns the list of mac addresses of the named pis.

              host setup WORKERS [LAPTOP]

                Executes the following steps

                    cms bridge create --interface='wlan0'
                    cms host key create red00[1-3]
                    cms host key gather red00[1-3],[email protected] keys.txt
                    cms host key scatter red00[1-3],localhost keys.txt
                    rm keys.txt
                    cms host tunnel create red00[1-3]

              host shutdown NAMES

                Shutsdown NAMES with `sudo shutdown -h now`. If localhost in
                names, it is shutdown last.

              host reboot NAMES

                Reboots NAMES with `sudo reboot`. If localhost in names,
                it is rebooted last.

              host adduser NAMES USER

                Adds a user with user name USER to the hosts identified by
                NAMES. Password is disabled, see host passwd to enable.

              host addsudo NAMES USER

                Adds sudo rights to USER at NAMES

              host passwd NAMES USER

                Changes the password for USER at NAMES

              host deluser NAMES USER

                Deleted USER from NAMES. Home directory will be removed.

              host config proxy PROXY NAMES

                This adds to your ~/.ssh/config file a ProxyJump
                configuration to reach NAMES via PROXY. This is useful when
                the PROXY is acting as a network bridge for NAMES to your
                current device.

                Example:
                    cms host config proxy [email protected] red00[1-2]
        """
        def _print(results):
            arguments.output = arguments.output or 'table'

            if arguments.output in ['table', 'yaml']:
                print(
                    Printer.write(
                        results,
                        order=['host', 'success', 'stdout', 'stderr'],
                        output=arguments.output))
            else:
                pprint(results)

        def _print_pis(results):
            arguments.output = arguments.output or 'table'

            if arguments.output in ['table', 'yaml']:
                print(
                    Printer.write(results,
                                  order=[
                                      'name', 'ip', 'user', 'os', 'mac',
                                      'model', 'memory', 'serial', ".local"
                                  ],
                                  output=arguments.output))
                # not printed         "revision"
                # not printed         "hardware"
            else:
                pprint(results)

        def get_filename(filename, hosts):
            if filename is not None:
                return filename
            if type(hosts) == str:
                hosts = Parameter.expand(hosts)
            label = hosts[0]
            return path_expand(f"~/.ssh/cluster_keys_{label}")

        map_parameters(arguments, 'eth', 'wlan'
                       'dryrun', 'output', 'user', 'port', 'append',
                       'StrictHostKeyChecking', 'local', 'proxy', 'ips',
                       'cluster')
        dryrun = arguments.dryrun

        # VERBOSE(arguments)

        if dryrun:
            VERBOSE(arguments)

        if arguments.info:

            names = Parameter.expand(arguments.names)

            # check if .local
            # check if mesh network
            # check if static network

            # use arp - a di find hosts ips
            # if linux
            #    dig +short -x  192.168.50.1

            Console.error("Not yet Implemented")

        elif arguments.find:

            verbose = arguments["--verbose"]

            names = Parameter.expand(arguments.NAMES)

            # temporary so we can easy modify while not needing to update cloudmesh.common
            from cloudmesh.host.network import PiNetwork

            network = PiNetwork()

            pis = network.find_pis(user=arguments.user, verbose=verbose)
            if arguments["--json"]:
                print(pis)
            else:
                _print_pis(pis)

        elif arguments.mac:

            names = Parameter.expand(arguments.NAMES)

            if not arguments.eth and not arguments.wlan:
                arguments.eth = True
                arguments.wlan = True

            eth = 'cat /sys/class/net/eth0/address'
            wlan = 'cat /sys/class/net/wlan0/address'
            if arguments.eth:
                results = Host.ssh(hosts=names,
                                   command=eth,
                                   username=arguments.user)
                print("eth0:")
                _print(results)

            if arguments.wlan:

                results = Host.ssh(hosts=names,
                                   command=wlan,
                                   username=arguments.user)
                print("wlan0:")
                _print(results)

        elif arguments.setup:

            HostCreate.setup(workers=arguments.WORKERS,
                             laptop=arguments.LAPTOP)

        elif arguments.scp and not arguments.key:

            result = Host.put(hosts=arguments.NAMES,
                              source=arguments.SOURCE,
                              destination=arguments.DESTINATION)

            _print(result)

        elif arguments.ping:
            names = Parameter.expand(arguments.NAMES)

            # print (names)

            results = Host.ping(hosts=names)

            _print(results)

        elif arguments.ssh:
            names = Parameter.expand(arguments.NAMES)

            # print (names)

            results = Host.ssh(hosts=names, command=arguments.COMMAND)
            _print(results)

        elif arguments.key and arguments.create:

            results = Host.ssh_keygen(hosts=arguments.NAMES,
                                      username=arguments.user,
                                      dryrun=dryrun)

            _print(results)

        elif arguments.key and arguments.list:

            names = Parameter.expand(arguments.NAMES)

            results = Host.ssh(hosts=names,
                               command='cat .ssh/id_rsa.pub',
                               username=arguments.user)

            _print(results)

        elif arguments.key and arguments.add:
            filename = get_filename(arguments.NAMES)
            if not os.path.isfile(filename):
                Console.error(f"Cannot find file {filename}")
                return

            # Copy to temp location
            Host.put(hosts=arguments.NAMES,
                     source=filename,
                     destination="~/.ssh/key.tmp")
            # Execute append command and remove command
            command = 'cat ~/.ssh/key.tmp >> ~/.ssh/authorized_keys && rm ~/.ssh/key.tmp'
            Host.ssh(hosts=arguments.NAMES, command=command)

        elif arguments.key and arguments.delete:
            Console.ok("key delete")
            filename = get_filename(arguments.FILE, arguments.NAMES)
            if not os.path.isfile(filename):
                Console.error(f"Cannot find file {filename}")
                return
            # Copy to temp location
            remote_temp = "~/.ssh/key.tmp"
            Host.put(hosts=arguments.NAMES,
                     source=filename,
                     destination=remote_temp)
            # grep can read multiple patterns from a file, one per line. Combine with
            # the options -v to output non-matching lines, and -F to match strings
            # instead of regex and -x to require that the whole line matches.
            command = f"""grep -Fvx -f {remote_temp} ~/.ssh/authorized_keys >remaining_keys && \
            mv remaining_keys ~/.ssh/authorized_keys && \
            rm {remote_temp}"""
            Host.ssh(hosts=arguments.NAMES, command=command)
            Console.ok(f"Delete keys from {filename} on {arguments.NAMES}")

        elif arguments.key and arguments.setup:

            label = Parameter.expand(arguments.NAMES)[0]
            filename = get_filename(arguments.FILE, arguments.NAMES)
            directory = os.path.dirname(filename)

            if directory:
                Shell.mkdir(directory)

            output = Host.gather_keys(username=arguments.user,
                                      hosts=arguments.NAMES,
                                      filename="~/.ssh/id_rsa.pub",
                                      key="~/.ssh/id_rsa",
                                      processors=3,
                                      dryrun=False)

            with open(filename, "w") as f:
                f.write(output)

            # scatter
            # place .ssh/config a trict host check to no

        elif arguments.key and arguments.gather:

            output = Host.gather_keys(username=arguments.user,
                                      hosts=arguments.NAMES,
                                      filename="~/.ssh/id_rsa.pub",
                                      key="~/.ssh/id_rsa",
                                      processors=3,
                                      dryrun=False)

            VERBOSE(arguments)

            filename = get_filename(arguments.FILE, arguments.NAMES)

            print(output)

            banner(f"Writing Keys to file {filename}")

            directory = os.path.dirname(filename)
            print('command directory', directory)
            if directory:
                Shell.mkdir(directory)

            if os.path.isfile(filename) and yn_choice(
                    f'{filename} is not empty. Do you wish to overwrite it? (If no you will append).'
            ):
                with open(filename, "w") as f:
                    f.write(output)
            else:
                with open(filename, "a") as f:
                    f.write(output)

        elif arguments.key and arguments.scatter:

            #
            # this should be a function in Host
            #

            filename = get_filename(arguments.FILE, arguments.NAMES)

            names = arguments.NAMES
            user = arguments.user

            if not os.path.isfile(filename):
                Console.error("The file does not exist")
                return ""

            if not user:
                result = Host.put(hosts=names,
                                  source=filename,
                                  destination=".ssh/authorized_keys")

                _print(result)
            else:
                Console.info('SCP to ./temp_authorzied_keys_temp')
                result = Host.put(hosts=names,
                                  source=filename,
                                  destination="temp_authorized_keys_temp")
                _print(result)

                Console.info(f'Mkdir /home/{user}/.ssh if not exist')
                command = f'sudo mkdir -p /home/' \
                          f'{user}/.ssh/'
                result = Host.ssh(hosts=names, command=command)
                _print(result)

                Console.info(f'Chown /home/{user}/.ssh to {user}')
                command = f'sudo chown {user}:{user} /home/' \
                          f'{user}/.ssh/'
                result = Host.ssh(hosts=names, command=command)
                _print(result)

                Console.info(f'Chmod /home/{user}/.ssh to 700')
                command = f'sudo chmod 700 /home/' \
                          f'{user}/.ssh/'
                result = Host.ssh(hosts=names, command=command)
                _print(result)

                Console.info(f'Mv temp_authorized_keys_temp to /home/'
                             f'{user}/.ssh/authorized_keys')
                command = f'sudo mv temp_authorized_keys_temp /home/' \
                          f'{user}/.ssh/authorized_keys'
                result = Host.ssh(hosts=names, command=command)
                _print(result)

                Console.info(f'Chown /home/{user}/.ssh/authorized_keys to '
                             f'{user}')
                command = f'sudo chown {user}:{user} /home/' \
                          f'{user}/.ssh/authorized_keys'
                result = Host.ssh(hosts=names, command=command)
                _print(result)

        elif arguments.key and arguments.access:

            #
            # this should be a function in Host
            #

            names = arguments.NAMES
            user = arguments.user

            filename = arguments.FILE
            temp = path_expand("~/.cloudmesh/temp_config")

            if filename:
                config = readfile(filename)
            else:
                config = textwrap.dedent("""
                Host *
                    StrictHostKeyChecking no
                """).strip()
            writefile(temp, config)

            if not os.path.isfile(temp):
                Console.error("The file does not exist")
                return ""

            if not user:
                result = Host.put(hosts=names,
                                  source=temp,
                                  destination=".ssh/config")

                _print(result)
            else:
                Console.info(f'Mkdir /home/{user}/.ssh if not exist')
                command = f'sudo mkdir -p /home/' \
                          f'{user}/.ssh/'
                result = Host.ssh(hosts=names, command=command)
                _print(result)

                Console.info('SCP to ./temp_config')
                result = Host.put(hosts=names,
                                  source=temp,
                                  destination=".ssh/config")
                _print(result)

                Console.info(f'Chown /home/{user}/.ssh to {user}')
                command = f'sudo chown {user}:{user} /home/' \
                          f'{user}/.ssh/'
                result = Host.ssh(hosts=names, command=command)
                _print(result)

                Console.info(f'Chmod /home/{user}/.ssh to 700')
                command = f'sudo chmod 700 /home/' \
                          f'{user}/.ssh/'
                result = Host.ssh(hosts=names, command=command)
                _print(result)

        elif arguments.config and arguments.ips:

            print("NNNNNNNN")

            key = arguments.key or "~/.ssh/id_rsa.pub"
            result = Host.config(hosts=arguments.NAMES,
                                 ips=arguments.IPS,
                                 username=arguments.user,
                                 key=key)
            print(result)
            """
            host config NAMES --ips=IPS [--user=USER] [--key=PUBLIC]
            host config --proxy=PROXY NAMES [--user=USER] [--append] [--local=no] [--StrictHostKeyChecking=no]
            host config NAMES [--user=USER] [--append] [--local=no] [--StrictHostKeyChecking=no]
            """

        elif arguments.config:

            if str_bool(arguments.local):
                local_str = ".local"
            else:
                local_str = ""

            if str_bool(arguments.StrictHostKeyChecking):
                strict_host_str = "yes"
            else:
                strict_host_str = "no"

            names = Parameter.expand(arguments.NAMES)
            user = arguments.user
            if arguments.cluster:
                cluster = arguments.cluster
            else:
                # take the first name and remove spaces
                cluster = ''.join([i for i in names[0] if not i.isdigit()])

            ssh_config_output = ""
            ssh_config_output = f'\n##### CLOUDMESH PROXY CONFIG {cluster} #####\n\n'

            if arguments.proxy:
                proxy_host = arguments.proxy
                proxy_jump = f'     ProxyJump {proxy_host}\n'
                ssh_config_output += \
                                    f'Host {proxy_host}\n' \
                                    f'     HostName {proxy_host}{local_str}\n' \
                                    f'     User {user}\n' \
                                    f'     PreferredAuthentications publickey\n' + \
                                    f'     StrictHostKeyChecking {strict_host_str}\n'
                ssh_config_output += '\n'

            else:
                proxy_jump = ""
            """
            ssh_config_output = f'\n##### CLOUDMESH PROXY CONFIG {cluster} #####\n\n'\
                                f'Host {proxy_host}\n' \
                                f'     HostName {proxy_host}{local_str}\n' \
                                f'     User {user}\n' \
                                f'     StrictHostKeyChecking {strict_host_str}\n\n'
            """

            ### the local_str in the hostname may be wrong since its not manager
            for name in names:
                ssh_config_template = f'Host {name}\n' \
                                      f'     HostName {name}{local_str}\n' \
                                      f'     User {user}\n' \
                                      f'     PreferredAuthentications publickey\n' + \
                                      f'     StrictHostKeyChecking {strict_host_str}\n' + \
                                      proxy_jump

                ssh_config_template += '\n'

                ssh_config_output += ssh_config_template

            ssh_config_output += f'##### CLOUDMESH PROXY CONFIG {cluster} #####\n'

            print('Adding to ~/.ssh/config')
            print(ssh_config_output)

            if not os.path.exists(path_expand('~/.ssh/config')):
                with open(path_expand('~/.ssh/config'), 'w+') as f:
                    f.write(ssh_config_output)
            else:
                f = open(path_expand('~/.ssh/config'), 'r')
                lines = f.readlines()
                f.close()
                with open(path_expand('~/.ssh/config'), 'w+') as f:
                    if f'##### CLOUDMESH PROXY CONFIG {cluster} #####\n' in lines:
                        start = lines.index(
                            f'##### CLOUDMESH PROXY CONFIG {cluster} #####\n')
                        lines.reverse()
                        end = lines.index(
                            f'##### CLOUDMESH PROXY CONFIG {cluster} #####\n')
                        end = len(lines) - end - 1
                        lines.reverse()
                        original_config = lines[start:end + 1]
                        del lines[start:end + 1]
                        f.writelines(lines)
                        if arguments.append:
                            f.writelines(original_config)
                            f.write(ssh_config_output)
                        else:
                            f.write(ssh_config_output)
                    else:
                        f.writelines(lines)
                        f.write(ssh_config_output)
                        f.close()

        elif arguments.check:

            key = arguments.key or "~/.ssh/id_rsa.pub"
            result = Host.check(hosts=arguments.NAMES,
                                username=arguments.user,
                                key=key)
            for entry in result:
                entry['success'] = entry['stdout'] == entry['host']

            _print(result)

        elif arguments.tunnel and arguments.create:

            wlan_ip = Shell.run("hostname -I | awk '{print $2}'").strip()
            print(f'\nUsing wlan0 IP = {wlan_ip}')
            hostname = Shell.run("hostname").strip()
            print(f'Using cluster hostname = {hostname}')

            names = Parameter.expand(arguments.NAMES)
            port = arguments.port or "8001"
            ssh_config_output = f'Host {hostname}\n' \
                                f'     HostName {hostname}.local\n' \
                                f'     User pi\n\n'

            for name in names:
                service_name = f"autossh-{name}.service"

                service_template = "[Unit]\n" \
                                   f"Description=AutoSSH tunnel service to {name} on local port " \
                                   f"{port}\n" \
                                   "After=multi-user.target\n\n" \
                                   "[Service]\n" \
                                   "User=pi\n" \
                                   "Group=pi\n" \
                                   'Environment="AUTOSSH_GATETIME=0"\n' \
                                   'ExecStart=/usr/bin/autossh -M 0 -o "ServerAliveInterval 30" ' \
                                   '-o "ServerAliveCountMax 3" -i ' \
                                   '/home/pi/.ssh/id_rsa -NL ' \
                                   f'{wlan_ip}:{port}:localhost:22 p' \
                                   f'i@{name}\n\n' \
                                   "[Install]\n" \
                                   "WantedBy=multi-user.target"

                ssh_config_template = f'Host {name}\n' \
                                      f'     HostName {hostname}.local\n' \
                                      f'     User pi\n' \
                                      f'     Port {port}\n\n'

                ssh_config_output += ssh_config_template
                Sudo.writefile(f'/etc/systemd/system/{service_name}',
                               service_template)
                port = str(int(port) + 1)

            os.system('sudo systemctl daemon-reload')
            for name in names:
                servicename = f"autossh-{name}.service"
                os.system(f'sudo systemctl start {servicename}')
                os.system(f'sudo systemctl enable {servicename}')

            print('\nTunnels created.\n\nPlease place the following in your '
                  'remote machine\'s (i.e. laptop) ~/.ssh/config file to '
                  'alias simple ssh access (i.e. ssh red001).')
            banner('copy to ~/.ssh/config on remote host (i.e laptop)')
            print(ssh_config_output)

        elif arguments.shutdown or arguments.reboot:

            if arguments.shutdown:
                command = 'sudo shutdown -h now'
            elif arguments.reboot:
                command = 'sudo reboot'

            names = Parameter.expand(arguments.NAMES)
            hostname = Shell.run("hostname").strip()

            localhost = None
            if "localhost" in names:
                names.remove("localhost")
                localhost = True
            if hostname in names:
                names.remove(hostname)
                localhost = True

            manager, workers = Host.get_hostnames(names)

            if workers:
                Console.info(f'Executing `{command}` for {workers}')
                Host.ssh(hosts=workers, command=command)

            if manager:
                Console.info(f'Executing `{command}` for {manager}')
                Host.ssh(hosts=manager, command=command)

            #_print(results)
            # results can be misleading becuase there is a race between the
            # shutdown and the error code being returned from the ssh processes.

            if localhost:
                os.system(command)

        elif arguments.adduser:
            names = Parameter.expand(arguments.NAMES)
            user = arguments.USER

            localhost = None
            if 'localhost' in names:
                localhost = 'localhost'
            elif platform.node() in names:
                localhost = platform.node()

            if localhost in names:
                print('\nAdding user to localhost')
                result = Shell.run(f'sudo adduser {user} '
                                   f'--disabled-password '
                                   f'--gecos "" ')
                print(result)
                names.remove(localhost)

            if len(names) > 0:
                command = f"sudo adduser {user} --disabled-password --gecos ',' "
                results = Host.ssh(hosts=names, command=command)
                _print(results)

        elif arguments.passwd:
            names = Parameter.expand(arguments.NAMES)
            user = arguments.USER

            localhost = None
            if 'localhost' in names:
                localhost = 'localhost'
            elif platform.node() in names:
                localhost = platform.node()

            if localhost in names:
                print("\nSetting password on localhost, please provide user "
                      "password")
                result = os.system(f'sudo passwd {user}')
                print(result)
                names.remove(localhost)

            if len(names) > 0:
                print("\nSetting password on remote hosts, please enter user "
                      "password\n")
                password = getpass("Please enter the user password:"******"{password}\n{password}" | sudo passwd {user}'
                results = Host.ssh(hosts=names, command=command)
                _print(results)

        elif arguments.addsudo:
            names = Parameter.expand(arguments.NAMES)
            user = arguments.USER

            localhost = None
            if 'localhost' in names:
                localhost = 'localhost'
            elif platform.node() in names:
                localhost = platform.node()

            if localhost in names:
                print('\nAdding user to sudo group on localhost')
                result = Shell.run(f'sudo adduser {user} sudo')
                print(result)
                names.remove(localhost)

            if len(names) > 0:
                command = f'sudo adduser {user} sudo'
                results = Host.ssh(hosts=names, command=command)
                _print(results)

        elif arguments.deluser:
            names = Parameter.expand(arguments.NAMES)
            user = arguments.USER

            if 'localhost' in names:
                localhost = 'localhost'
            elif platform.node() in names:
                localhost = platform.node()

            if localhost in names:
                print('\nDeleting user on localhost')
                result = Shell.run(f'sudo userdel -r {user}')
                print(result)
                names.remove(localhost)

            if len(names) > 0:
                command = f'sudo userdel -r {user}'
                results = Host.ssh(hosts=names, command=command)
                _print(results)

        return ""