Check-in [7d648e5be3]
Not logged in
Overview
Comment:Fix quoting of arguments of start command. Support per user configuration in .config/vws/vws.conf as well as .vwsrc Fix some pylint warnings
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 7d648e5be3cc98ccc00e286915c4ff7ee4428799
User & Date: vitus on 2022-02-08 17:27:14
Other Links: manifest | tags
Context
2022-07-28
15:22
Version 0.8.1 - support new info network format check-in: e5904254e7 user: vitus tags: trunk
2022-02-08
17:27
Fix quoting of arguments of start command. Support per user configuration in .config/vws/vws.conf as well as .vwsrc Fix some pylint warnings check-in: 7d648e5be3 user: vitus tags: trunk
2020-01-16
18:18
Fix moninor command for python3 check-in: 9be36c3e65 user: vitus tags: trunk
Changes

Modified vws from [0a5a51e7d3] to [c5b995c8c3].

1
2
3
4
5
6
7
8
9
10
11
12
13



14
15
16

17
18
19
20
21
22
23
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
""" vws - script to control QEMU/KVM virtual workstations """
# pylint: disable=too-many-lines
from configparser import ConfigParser
from argparse import ArgumentParser, Namespace
import fcntl
import socket
import select
import errno
import re
import os
import os.path



import sys
import time
import pwd


VERSION = 0.8
def find_vm(name):
    """ Search and return VM directory """
    search_path = [os.path.join(pwd.getpwuid(os.getuid()).pw_dir, "VWs"),
                   config.get("directories", "SharedVMs"),
                   config.get("directories", "AutostartVMs")]













>
>
>



>







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
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
""" vws - script to control QEMU/KVM virtual workstations """
# pylint: disable=too-many-lines
from configparser import ConfigParser
from argparse import ArgumentParser, Namespace
import fcntl
import socket
import select
import errno
import re
import os
import os.path
import fnmatch
import shlex
import shutil
import sys
import time
import pwd
import grp

VERSION = 0.8
def find_vm(name):
    """ Search and return VM directory """
    search_path = [os.path.join(pwd.getpwuid(os.getuid()).pw_dir, "VWs"),
                   config.get("directories", "SharedVMs"),
                   config.get("directories", "AutostartVMs")]
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
    try:
        sock.connect(monitor_path)
    except IOError as ex:
        if ex.errno == errno.ECONNREFUSED:
            # virtal machine is not running
            return None
        raise ex
    readfd, dummy_w, dummy_x = select.select([sock], [], [], 0.1)
    if sock in readfd:
        dummy_greeting = sock.recv(1024)
    return sock

def send_command(sock, command):
    """ Sends monitor command to given socket and returns answer """
    if sock is None:
        raise RuntimeError("None socket is passed to send_command")
    fcntl.flock(sock, fcntl.LOCK_EX)







|

|







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
    try:
        sock.connect(monitor_path)
    except IOError as ex:
        if ex.errno == errno.ECONNREFUSED:
            # virtal machine is not running
            return None
        raise ex
    readfd, _, _ = select.select([sock], [], [], 0.1)
    if sock in readfd:
        _ = sock.recv(1024)
    return sock

def send_command(sock, command):
    """ Sends monitor command to given socket and returns answer """
    if sock is None:
        raise RuntimeError("None socket is passed to send_command")
    fcntl.flock(sock, fcntl.LOCK_EX)
226
227
228
229
230
231
232
233
234

235
236
237
238
239
240
241


242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
    return None

def make_start_cmdline(options):
    """
    Append options passed from vws commandline to start script
    commandline
    """
    arg = ""
    if options.cdrom:

        arg = " -cdrom " + os.path.abspath(options.cdrom[0])
    if options.snapshot:
        arg = arg+" -snapshot"
    if options.args:
        arg = arg + " " + "".join(options.args)
    if options.password:
        os.environ["SPICE_PASSWORD"] = options.password[0]


    return arg
def cmd_start(options):
    """ vws start """
    if not "DISPLAY" in os.environ:
        # If cannot start GUI just don't do it.
        options.gui = False
    if options.stopped:
        arg = make_start_cmdline(options)
        cwd = os.getcwd()
        os.chdir(options.dir)
        # Check for snapshot
        snapshot_id = check_for_snapshot(options.dir)
        if snapshot_id is not None:
            arg = arg + " -loadvm " + snapshot_id
        # Check for correct brige name
        try:
            os.stat("monitor")
        except FileNotFoundError:
            # We cannot find monitor socket. So this machine might be
            # never run on this host
            fix_bridge_name("start")







|

>
|

|

|


>
>
|












|







230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
    return None

def make_start_cmdline(options):
    """
    Append options passed from vws commandline to start script
    commandline
    """
    arg = []
    if options.cdrom:
        arg.append("-cdrom")
        arg.append(os.path.abspath(options.cdrom[0]))
    if options.snapshot:
        arg.append("-snapshot")
    if options.args:
        arg.append( "".join(options.args))
    if options.password:
        os.environ["SPICE_PASSWORD"] = options.password[0]
    if arg:
        return shlex.join(arg)
    return ""
def cmd_start(options):
    """ vws start """
    if not "DISPLAY" in os.environ:
        # If cannot start GUI just don't do it.
        options.gui = False
    if options.stopped:
        arg = make_start_cmdline(options)
        cwd = os.getcwd()
        os.chdir(options.dir)
        # Check for snapshot
        snapshot_id = check_for_snapshot(options.dir)
        if snapshot_id is not None:
            arg = arg + " " + shlex.join(["-loadvm", snapshot_id])
        # Check for correct brige name
        try:
            os.stat("monitor")
        except FileNotFoundError:
            # We cannot find monitor socket. So this machine might be
            # never run on this host
            fix_bridge_name("start")
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
                idx = answer.index('\n')
                print(answer[idx+1:], end="")
                eol = answer.endswith("\n")
                sys.stdout.flush()
            elif options.sock in readfd:
                print("UNSOLICITED MESSAGE %" +
                      options.sock.recv(1000).decode("utf-8").rstrip())
                eol = True      
    except KeyboardInterrupt:
        if not eol:
            print("")
        eol = True
        print("Keyboard interrupt")
    if not eol:
        print("")







|







321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
                idx = answer.index('\n')
                print(answer[idx+1:], end="")
                eol = answer.endswith("\n")
                sys.stdout.flush()
            elif options.sock in readfd:
                print("UNSOLICITED MESSAGE %" +
                      options.sock.recv(1000).decode("utf-8").rstrip())
                eol = True
    except KeyboardInterrupt:
        if not eol:
            print("")
        eol = True
        print("Keyboard interrupt")
    if not eol:
        print("")
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
        arp_data.update(parse_arp(bridge))
    for vminfo in listing:
        if "mac" in vminfo and not "ip" in vminfo:
            if vminfo["mac"] in arp_data:
                vminfo["ip"] = arp_data[vminfo["mac"]]
            else:
                vminfo["ip"] = "-"
def all_vms(patterns=["*"]):
    """
    Returns list of tuples  vmname, vmtype, directory
    for all vms
    """
    import fnmatch
    search_path = [("private", os.path.join(pwd.getpwuid(os.getuid()).pw_dir,
                                            "VWs")),
                   ("shared", config.get("directories", "SharedVMs")),
                   ("autostart", config.get("directories", "AutostartVMs"))]
    vmlist = []
    for (vmtype, dirname) in search_path:
        if not os.access(dirname, os.X_OK):







|




<







483
484
485
486
487
488
489
490
491
492
493
494

495
496
497
498
499
500
501
        arp_data.update(parse_arp(bridge))
    for vminfo in listing:
        if "mac" in vminfo and not "ip" in vminfo:
            if vminfo["mac"] in arp_data:
                vminfo["ip"] = arp_data[vminfo["mac"]]
            else:
                vminfo["ip"] = "-"
def all_vms(patterns=("*",)):
    """
    Returns list of tuples  vmname, vmtype, directory
    for all vms
    """

    search_path = [("private", os.path.join(pwd.getpwuid(os.getuid()).pw_dir,
                                            "VWs")),
                   ("shared", config.get("directories", "SharedVMs")),
                   ("autostart", config.get("directories", "AutostartVMs"))]
    vmlist = []
    for (vmtype, dirname) in search_path:
        if not os.access(dirname, os.X_OK):
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
        else:
            print(f["name"])
    if not count:
        sys.exit(1)

def cmd_screenshot(options):
    """ vws screenshot """
    from os.path import abspath
    filename = abspath(options.filename)
    print(send_command(options.sock, "screendump " + filename))

def cmd_record(options):
    """ vws record """
    from os.path import abspath
    filename = abspath(options.filename)
    print(send_command(options.sock, "wavcapture " + filename))

def cmd_stoprecord(options):
    """ vws stoprecord """
    answer = send_command(options.sock, "info capture")
    match = re.search('\\[(\\d+)\\]: ', answer)
    if not match:







<
|




<
|







534
535
536
537
538
539
540

541
542
543
544
545

546
547
548
549
550
551
552
553
        else:
            print(f["name"])
    if not count:
        sys.exit(1)

def cmd_screenshot(options):
    """ vws screenshot """

    filename = os.path.abspath(options.filename)
    print(send_command(options.sock, "screendump " + filename))

def cmd_record(options):
    """ vws record """

    filename = os.path.abspath(options.filename)
    print(send_command(options.sock, "wavcapture " + filename))

def cmd_stoprecord(options):
    """ vws stoprecord """
    answer = send_command(options.sock, "info capture")
    match = re.search('\\[(\\d+)\\]: ', answer)
    if not match:
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
    print("")

def cmd_shutdown(options):
    """ Search for all running machines and stops all of them """
    dirlist = [config.get("directories", "AutostartVMs"),
               config.get("directories", "SharedVms")]
    if os.getresuid()[1] == 0:
        import grp
        dirlist += map(lambda x: os.path.expanduser("~"+x)+"/VWs",
                       grp.getgrnam(config.get("permissions",
                                               "vm_group")).gr_mem)
    else:
        dirlist.append(os.path.expanduser("~")+"/VWs")
    count = 1 #Fake positive values for there is not postcondition loop in the python
    forced_finish = time.time() + options.timeout







<







712
713
714
715
716
717
718

719
720
721
722
723
724
725
    print("")

def cmd_shutdown(options):
    """ Search for all running machines and stops all of them """
    dirlist = [config.get("directories", "AutostartVMs"),
               config.get("directories", "SharedVms")]
    if os.getresuid()[1] == 0:

        dirlist += map(lambda x: os.path.expanduser("~"+x)+"/VWs",
                       grp.getgrnam(config.get("permissions",
                                               "vm_group")).gr_mem)
    else:
        dirlist.append(os.path.expanduser("~")+"/VWs")
    count = 1 #Fake positive values for there is not postcondition loop in the python
    forced_finish = time.time() + options.timeout
843
844
845
846
847
848
849
850
851
852
853
854

855
856
857
858
859
860
861
862
863
864
865
866

    options["memory"] = parsed_args.mem
    if parsed_args.localtime:
        options["rtc"] = "-rtc base=localtime,clock=host \\\n"

    if os.path.exists(machinedir):
        if os.path.exists(os.path.join(machinedir, "start")):
            raise OSError("Virtual Worstation %s already exists" %
                          parsed_args.machine)
        else:
            raise OSError("Cannot create VW directory, " +
                          "something on the way")

    #  Creating directory for VM
    os.makedirs(machinedir, dirmode)
    parsed_args.dir = machinedir
    if parsed_args.shared:
        import grp
        gid = grp.getgrnam(config.get("permissions", "vm_group")).gr_gid
        uid = os.getuid()
        os.chown(machinedir, uid, gid)
        if config.getboolean("permissions", "setgid_vm"):
            os.chmod(machinedir, 0o2775)
    driveopts = {"interface":parsed_args.diskif, "image":drivename}
    if parsed_args.install:







|


|

>




<







846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862

863
864
865
866
867
868
869

    options["memory"] = parsed_args.mem
    if parsed_args.localtime:
        options["rtc"] = "-rtc base=localtime,clock=host \\\n"

    if os.path.exists(machinedir):
        if os.path.exists(os.path.join(machinedir, "start")):
            exc = OSError("Virtual Worstation %s already exists" %
                          parsed_args.machine)
        else:
            exc = OSError("Cannot create VW directory, " +
                          "something on the way")
        raise exc
    #  Creating directory for VM
    os.makedirs(machinedir, dirmode)
    parsed_args.dir = machinedir
    if parsed_args.shared:

        gid = grp.getgrnam(config.get("permissions", "vm_group")).gr_gid
        uid = os.getuid()
        os.chown(machinedir, uid, gid)
        if config.getboolean("permissions", "setgid_vm"):
            os.chmod(machinedir, 0o2775)
    driveopts = {"interface":parsed_args.diskif, "image":drivename}
    if parsed_args.install:
938
939
940
941
942
943
944

945

946
947
948
949
950
951
952
953
                    'permissions':{'vm_group':'kvm',
                                   'autostart_user':'root',
                                   'setgid_vm':'yes'}})

def read_config(conf):
    """ Read configration files """
    if os.getuid() != 0:

        conf.read(['/etc/vws.conf',

                   os.path.join(pwd.getpwuid(os.getuid()).pw_dir, '.vwsrc')])
    else:
        conf.read(['/etc/vws.conf'])
def main():
    """ Parse an arguments and execute everything """
    global config
    config_defaults(config)
    read_config(config)







>

>
|







941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
                    'permissions':{'vm_group':'kvm',
                                   'autostart_user':'root',
                                   'setgid_vm':'yes'}})

def read_config(conf):
    """ Read configration files """
    if os.getuid() != 0:
        home = pwd.getpwuid(os.getuid()).pw_dir
        conf.read(['/etc/vws.conf',
                   os.path.join(home, ".config", "vws", "vws.conf"),
                   os.path.join(home, '.vwsrc')])
    else:
        conf.read(['/etc/vws.conf'])
def main():
    """ Parse an arguments and execute everything """
    global config
    config_defaults(config)
    read_config(config)
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
    # Create command is totally different, so it is handled separately
    if parsed_args.command == 'create':
        try:
            cmd_create(parsed_args)
        except Exception as ex: # pylint: disable=broad-except
            print(str(ex), file=sys.stderr)
            if hasattr(parsed_args, "dir"):
                import shutil
                shutil.rmtree(parsed_args.dir)
                sys.exit(1)
        sys.exit(0)

    if parsed_args.command is None:
        args.print_help()
        sys.exit(0)







<







1093
1094
1095
1096
1097
1098
1099

1100
1101
1102
1103
1104
1105
1106
    # Create command is totally different, so it is handled separately
    if parsed_args.command == 'create':
        try:
            cmd_create(parsed_args)
        except Exception as ex: # pylint: disable=broad-except
            print(str(ex), file=sys.stderr)
            if hasattr(parsed_args, "dir"):

                shutil.rmtree(parsed_args.dir)
                sys.exit(1)
        sys.exit(0)

    if parsed_args.command is None:
        args.print_help()
        sys.exit(0)