forked from ansible/molecule
-
Notifications
You must be signed in to change notification settings - Fork 0
/
login.py
115 lines (99 loc) · 4.23 KB
/
login.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# Copyright (c) 2015-2017 Cisco Systems, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
import fcntl
import os
import pexpect
import signal
import struct
import sys
import termios
import click
from molecule import util
from molecule.command import base
class Login(base.Base):
def execute(self):
"""
Execute the actions necessary to perform a `molecule login` and
returns None.
>>> molecule login --host hotname --scenario-name foo
:return: None
"""
if not self._config.state.created:
msg = 'Instances not created. Please create instances first.'
util.print_error(msg)
util.sysexit()
hosts = [d['name'] for d in self._config.platforms_with_scenario_name]
hostname = self._get_hostname(hosts)
self._get_login(hostname)
def _get_hostname(self, hosts):
hostname = self._config.command_args.get('host')
match = [x for x in hosts if x.startswith(hostname)]
if len(match) == 0:
msg = ("There are no hosts that match '{}'. You "
'can only login to valid hosts.').format(hostname)
util.print_error(msg)
util.sysexit()
elif len(match) != 1:
# If there are multiple matches, but one of them is an exact string
# match, assume this is the one they're looking for and use it.
if hostname in match:
match = [hostname, ]
else:
msg = ("There are {} hosts that match '{}'. You "
'can only login to one at a time.\n\n'
'Available hosts:\n{}'.format(
len(match), hostname, '\n'.join(sorted(hosts))))
util.print_error(msg)
util.sysexit()
return match[0]
def _get_login(self, hostname): # pragma: no cover
login_cmd = self._config.driver.login_cmd_template
login_args = self._config.driver.login_args(hostname)
lines, columns = os.popen('stty size', 'r').read().split()
dimensions = (int(lines), int(columns))
self._pt = pexpect.spawn(
'/usr/bin/env ' + login_cmd.format(*login_args),
dimensions=dimensions)
signal.signal(signal.SIGWINCH, self._sigwinch_passthrough)
self._pt.interact()
def _sigwinch_passthrough(self, sig, data): # pragma: no cover
TIOCGWINSZ = 1074295912 # assume
if 'TIOCGWINSZ' in dir(termios):
TIOCGWINSZ = termios.TIOCGWINSZ
s = struct.pack('HHHH', 0, 0, 0, 0)
a = struct.unpack('HHHH',
fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ, s))
self._pt.setwinsize(a[0], a[1])
@click.command()
@click.pass_context
@click.option('--host', required=True, help='Host to access.')
@click.option(
'--scenario-name', required=True, help='Name of the scenario to target.')
def login(ctx, host, scenario_name): # pragma: no cover
""" Log in to one instance. """
args = ctx.obj.get('args')
command_args = {
'subcommand': __name__,
'host': host,
'scenario_name': scenario_name
}
for config in base.get_configs(args, command_args):
l = Login(config)
l.execute()