Merge ~alexmurray/ubuntu-security-tools:umt-qrt-vm-creation into ubuntu-security-tools:master

Proposed by Alex Murray
Status: Merged
Merged at revision: 8c069bc6697be2c93a169825801a23bec70443db
Proposed branch: ~alexmurray/ubuntu-security-tools:umt-qrt-vm-creation
Merge into: ubuntu-security-tools:master
Diff against target: 137 lines (+71/-16)
1 file modified
build-tools/umt (+71/-16)
Reviewer Review Type Date Requested Status
Ubuntu Security Team Pending
Review via email: mp+408462@code.launchpad.net

Description of the change

Add support to `umt qrt` to offer to automatically create a VM if none already exists.

To post a comment you must log in.
Revision history for this message
Seth Arnold (seth-arnold) wrote :

Cool :D I've got a comment on the lxd create(), but it's minor.

Thanks

Revision history for this message
Alex Murray (alexmurray) wrote :

Thanks for the review Seth - I made a chance on master last week https://git.launchpad.net/ubuntu-security-tools/commit/?id=95441826c758b1df62bcfcdaf73354003de62520 that should address your concern.

Revision history for this message
Seth Arnold (seth-arnold) wrote :

On Mon, Sep 20, 2021 at 05:09:39AM -0000, Alex Murray wrote:
> Thanks for the review Seth - I made a chance on master last week
> https://git.launchpad.net/ubuntu-security-tools/commit/?id=95441826c758b1df62bcfcdaf73354003de62520
> that should address your concern.

Oh, that looks a lot nicer. Thanks :)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/build-tools/umt b/build-tools/umt
2index 40b5250..50a0c19 100755
3--- a/build-tools/umt
4+++ b/build-tools/umt
5@@ -2079,20 +2079,34 @@ def cmd_search():
6 pkg_list[release][0], pkg_list[release][1], pkg_list[release][2]))
7
8 class QRTBackend:
9- class NotImplementedError(Exception):
10- def __init__(self, msg="🚧 Please implement me 🚧", *args, **kwargs):
11- super().__init__(msg, *args, **kwargs)
12-
13- def __init__(self, name, vm, user=os.getenv("USER"), debug=False, dry_run=False, no_snapshot=False):
14+ def __init__(self, name, vm, release, user=os.getenv("USER"), debug=False, dry_run=False, no_snapshot=False):
15 self.name = name
16 self.vm = vm
17+ self.release = release
18 self.user = user
19 self.debug = debug
20 self.dry_run = dry_run
21 self.no_snapshot = no_snapshot
22-
23- def _runcmd(self, cmd, okrc=[0]):
24- return runcmd(cmd, debug=self.debug, dry_run=self.dry_run, okrc=okrc)
25+ def _runcmd(self, cmd, stdout=subprocess.PIPE, okrc=[0]):
26+ return runcmd(cmd, stdout=stdout, debug=self.debug, dry_run=self.dry_run, okrc=okrc)
27+ def exists(self):
28+ '''Check whether the VM exists in the backend.'''
29+ raise NotImplementedError()
30+ def create(self):
31+ '''Create a new VM in the backend.'''
32+ assert(not self.exists())
33+ raise NotImplementedError()
34+ def ensure(self):
35+ '''Ensure the VM exists in the backend.'''
36+ if not self.exists():
37+ print("%s does not exist in %s backend, do you want to try and automatically create it (y/N)? " % (self.vm, self.name), end=' ', flush=True)
38+ answer = sys.stdin.readline().strip().lower()
39+ if answer == 'y':
40+ self.create()
41+ if not self.dry_run:
42+ assert(self.exists())
43+ else:
44+ raise RuntimeError("User choose not to proceed.")
45 def start(self):
46 raise NotImplementedError()
47 def stop(self, force=False):
48@@ -2148,10 +2162,31 @@ class UVTQRTBackend(QRTBackend):
49 err("Unable to find uvt - please define QRT as the path to ubuntu-qa-tools repo")
50 return uvt
51
52- def __init__(self, vm, user=os.getenv("USER"), debug=False, dry_run=False, no_snapshot=False):
53- super().__init__("uvt", vm, user, debug, dry_run, no_snapshot)
54+ def __init__(self, vm, release, user=os.getenv("USER"), debug=False, dry_run=False, no_snapshot=False):
55+ super().__init__("uvt", vm, release, user, debug, dry_run, no_snapshot)
56 self.uvt = self.find_uvt_binary()
57
58+ def exists(self):
59+ found = False
60+ cmd = [self.uvt, "list"]
61+ rc, report = self._runcmd(cmd)
62+ # look for an exact match so we don't accidentally match on common
63+ # prefixes
64+ for line in report.splitlines():
65+ # if VM is running will be suffixed with an asterisk
66+ if line == self.vm or line == self.vm + "*":
67+ found = True
68+ return found
69+
70+ def create(self):
71+ cmd = [self.uvt, "new", "--name", self.vm, "-f", self.release, "amd64"]
72+ # let stdout go to terminal
73+ rc, report = self._runcmd(cmd, stdout=None)
74+ if rc != 0:
75+ raise RuntimeError("Failed to create new UVT VM %s: %s" %
76+ (self.vm, report))
77+ assert(self.exists())
78+
79 def start(self):
80 # run headless since we don't use the GUI and wait for ssh
81 # availability
82@@ -2205,12 +2240,29 @@ class UVTQRTBackend(QRTBackend):
83
84
85 class LXDQRTBackend(QRTBackend):
86- def __init__(self, vm, user="ubuntu", debug=False, dry_run=False, no_snapshot=False):
87- super().__init__("lxd", vm, user, debug, dry_run, no_snapshot)
88+ def __init__(self, vm, release, user="ubuntu", debug=False, dry_run=False, no_snapshot=False):
89+ super().__init__("lxd", vm, release, user, debug, dry_run, no_snapshot)
90
91 def running(self):
92- (_, result) = self._runcmd(["lxc", "info", self.vm])
93- return "RUNNING" in result
94+ (rc, result) = self._runcmd(["lxc", "info", self.vm], okrc=None)
95+ return rc == 0 and "RUNNING" in result
96+
97+ def exists(self):
98+ cmd = ["lxc", "info", self.vm]
99+ rc, _ = self._runcmd(cmd, okrc=None)
100+ return rc == 0
101+
102+ def create(self):
103+ cmd = ["lxc", "launch", "--vm", "images:ubuntu/%s" % self.release, self.vm]
104+ # let stdout go to terminal
105+ rc, report = self._runcmd(cmd, stdout=None)
106+ if rc != 0:
107+ raise RuntimeError("Failed to create new LXD VM %s: %s" %
108+ (self.vm, report))
109+ # wait a bit since seems to take a while until the lxd-agent is
110+ # running and there doesn't appear to be a way to detect this...
111+ time.sleep(15)
112+ assert(self.exists())
113
114 def start(self):
115 if self.running():
116@@ -2445,9 +2497,10 @@ def run_qrt_tests(opt, args, details):
117 else:
118 vm = opt.vm_prefix + "-" + details["release"] + "-" + opt.arch
119
120- backend = qrt_backends[opt.backend](vm, user=user, debug=opt.debug, no_snapshot=opt.no_snapshot)
121+ backend = qrt_backends[opt.backend](vm, details["release"], user=user,
122+ debug=opt.debug, no_snapshot=opt.no_snapshot)
123
124- print("Running QRT test " + qrt_test + " for " + details["package"] + " as " + user + (" via sudo" if opt.sudo else "") + " with " + backend.name)
125+ print("Running QRT test " + qrt_test + " for " + details["package"] + " in release " + details["release"] + " as " + user + (" via sudo" if opt.sudo else "") + " with " + backend.name)
126
127 # catch any exception which occurred during execution
128 try:
129@@ -2467,6 +2520,8 @@ def run_qrt_tests(opt, args, details):
130 if not opt.dry_run:
131 copy_to_repo(opt, details, quiet=not opt.debug, source_dest=source_dest, binary_dest=binary_dest)
132
133+ backend.ensure()
134+
135 if not opt.no_update:
136 # ensure vm is stopped first - unless no_snapshot we can stop
137 # it by force

Subscribers

People subscribed via source and target branches