File: remote.py

package info (click to toggle)
libreswan 5.2-2.3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 81,644 kB
  • sloc: ansic: 129,988; sh: 32,018; xml: 20,646; python: 10,303; makefile: 3,022; javascript: 1,506; sed: 574; yacc: 511; perl: 264; awk: 52
file content (190 lines) | stat: -rw-r--r-- 6,409 bytes parent folder | download | duplicates (2)
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# Stuff to talk to virsh, for libreswan
#
# Copyright (C) 2015-2019  Andrew Cagney
# Copyright (C) 2020  Ravi Teja
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.  See <https://www.gnu.org/licenses/gpl2.txt>.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.

import re
import os
import random
import logging
import pexpect
import time

from fab import virsh
from fab import timing
from fab import logutil

BOOT_TIMEOUT = 120 # allow time to boot

LOGIN = rb'root'
LOGIN_PROMPT = rb'login: $'
LOGIN_TIMEOUT = 10

# sent login, expecting password

PASSWORD = rb'swan'
PASSWORD_PROMPT = rb'Password:\s?$'
PASSWORD_TIMEOUT = 10

# sent password, expecting shell

SHELL_TIMEOUT = 10

# expecting some sort of output
#
# - just sent username; expecting password
# - just sent enter; expecting anything

def _login(console, logger, login, password, lapsed_time, timeout):

    tries = 1

    while True:
        if tries > 2:
            logger.error("giving up after %s and %d attempts at logging in",
                         lapsed_time, tries)
            return None

        # Hopefully "Last login" is matched before "login: "
        try:
            match console.expect([LOGIN_PROMPT,
                                  PASSWORD_PROMPT,
                                  b'Last login',
                                  console.prompt],
                                 timeout=timeout):
                case 0: # Login: prompt
                    timeout = PASSWORD_TIMEOUT
                    logger.info("got %s prompt after %s; sending '%s' and waiting %s seconds for password prompt",
                                LOGIN_PROMPT, lapsed_time, login, timeout)
                    console.sendline(login)
                    tries = tries + 1
                case 1: # Password: prompt
                    timeout = SHELL_TIMEOUT
                    logger.info("got %s prompt after %s; sending '%s' and waiting %s seconds for shell prompt",
                                PASSWORD_PROMPT, lapsed_time, password, timeout)
                    console.sendline(password)
                case 2: # Last login: looks a lot like login: ulgh!  Skip.
                    logger.info("got 'Last login' after %s; ignoring", lapsed_time)
                case 3: # Shell prompt
                    logger.info("we're in (after %s)!", lapsed_time)
                    break # out of loop
        except pexpect.TIMEOUT:
            logger.error("TIMEOUT while trying to login")
            return None
        except pexpect.EOF:
            logger.error("EOF while trying to login")
            return None

    # Sync with the remote end by matching a known and unique pattern.
    # Strictly match PATTERN+PROMPT so that earlier prompts that might
    # also be lurking in the output are discarded.
    try:
        number = str(random.randrange(10000, 1000000))
        sync = "sync=" + number + "=cnyc"
        console.sendline("echo " + sync)
        console.expect(sync.encode() + rb'\s+' + console.prompt.pattern, timeout=virsh.TIMEOUT)
    except (pexpect.TIMEOUT, pexpect.EOF) as e:
        logger.error("EXCEPTION while syncing output: %s", e)
        return None

    # Set the PTY inside the VM to no-echo; kvmsh.py's interactive
    # mode will re-adjust this.
    #
    # This can barf with a timeout when the prompt is wrong.
    try:
        console.run("export TERM=dumb ; unset LS_COLORS ; stty sane -echo -onlcr")
    except (pexpect.TIMEOUT, pexpect.EOF) as e:
        logger.error("EXCEPTION while setting terminal mode: %s", e)
        return None

    return console

# The machine is assumed to be booted; but its state is unknown.

def login(domain, console, login=LOGIN, password=PASSWORD):

    logger = domain.logger
    if not console:
        domain.logger.error("domain not running")
        return None

    lapsed_time = timing.Lapsed()

    logger.info("hitting enter (control-c+carriage return)")
    console.sendintr()
    console.sendline("")

    # try to login
    if not _login(console, logger, login=login, password=password,
                  lapsed_time=lapsed_time, timeout=LOGIN_TIMEOUT):
        return None

    return console


def boot_to_login_prompt(domain):

    console = domain.start()
    try:
        match console.expect([LOGIN_PROMPT],
                             timeout=BOOT_TIMEOUT):
            case 0:
                domain.logger.info("domain reached Login: prompt")
                return console
    except pexpect.TIMEOUT:
        domain.logger.error("TIMEOUT waiting for Login: prompt")
        return None


def boot_and_login(domain):
    logger = domain.logger
    lapsed_time = timing.Lapsed()

    tries = 1
    while True:

        console = domain.start()
        if not console:
            logger.error("domain did not start (boot attempt %d and %s); giving up",
                         tries, lapsed_time)
            return None

        # wait for just the login prompt
        problem = None
        try:
            match console.expect([LOGIN_PROMPT], timeout=BOOT_TIMEOUT):
                case 0:
                    logger.info("boot successful (boot attempt %d and %s)",
                                tries, lapsed_time)
                    break # out of loop
        except pexpect.TIMEOUT:
            problem = "TIMEOUT"
        except pexpect.EOF:
            problem = "EOF"

        domain.destroy()
        if tries > 2:
            logger.error("%s waiting for Login: prompt (boot attempt %d and %s); giving up",
                         problem, tries, lapsed_time)
            return None
        logger.error("%s waiting for Login: prompt (boot attempt %d and %s); retrying",
                     problem, tries, lapsed_time)
        tries = tries + 1

    # start the login and then let _login() take over
    console.sendline(LOGIN)
    if not _login(console, logger, login=LOGIN, password=PASSWORD,
                  lapsed_time=lapsed_time, timeout=PASSWORD_TIMEOUT):
        return None

    return console