]>
Commit | Line | Data |
---|---|---|
a6a7fe20 JS |
1 | #!/usr/bin/python3 |
2 | ||
a6a7fe20 | 3 | import libvirt |
1ed8ca9f | 4 | import logging |
6632e137 JS |
5 | import os |
6 | import xml.etree.ElementTree as ET | |
7 | ||
b560f31a JS |
8 | from . import disk |
9 | from . import serial_connection | |
1ed8ca9f JS |
10 | |
11 | logger = logging.getLogger("nitsi.machine") | |
a6a7fe20 | 12 | |
ee227ea1 | 13 | class Machine(): |
7005787e | 14 | def __init__(self, libvirt_con, vm_xml_file, snapshot_xml_file, image, root_uid, username, password): |
1ed8ca9f | 15 | self.log = logger.getChild(os.path.basename(vm_xml_file)) |
7005787e | 16 | self.con = libvirt_con |
5499746c JS |
17 | # self.dom should be always defined |
18 | self.dom = None | |
19 | # self.snapshot should be also at least None | |
20 | self.snapshot = None | |
21 | ||
22 | ||
a6a7fe20 JS |
23 | try: |
24 | with open(vm_xml_file) as fobj: | |
25 | self.vm_xml = fobj.read() | |
26 | except FileNotFoundError as error: | |
341e7373 JS |
27 | logger.error("No such file: {}".format(vm_xml_file)) |
28 | ||
29 | try: | |
30 | self.name = self.get_name() | |
31 | except BaseException as error: | |
32 | logger.error("Could not get name of the machine: {}".format(vm_xml_file)) | |
33 | raise error | |
34 | ||
35 | self.log = logger.getChild(self.name) | |
36 | self.log.debug("Name of this machine is {}".format(self.name)) | |
a6a7fe20 JS |
37 | |
38 | try: | |
39 | with open(snapshot_xml_file) as fobj: | |
40 | self.snapshot_xml = fobj.read() | |
41 | except FileNotFoundError as error: | |
42 | self.log.error("No such file: {}".format(snapshot_xml_file)) | |
43 | ||
44 | self.image = image | |
45 | ||
46 | if not os.path.isfile(self.image): | |
47 | self.log.error("No such file: {}".format(self.image)) | |
48 | ||
49 | self.root_uid = root_uid | |
ee227ea1 | 50 | self.disk = disk.Disk(image) |
a6a7fe20 JS |
51 | |
52 | self.username = username | |
53 | self.password = password | |
54 | ||
55 | def define(self): | |
5499746c | 56 | self.log.info("Defining virtual machine") |
8b744f18 | 57 | self.dom = self.con.defineXML(self.vm_xml) |
a6a7fe20 JS |
58 | if self.dom == None: |
59 | self.log.error("Could not define VM") | |
60 | raise BaseException | |
61 | ||
62 | def start(self): | |
5499746c | 63 | self.log.info("Starting virtual machine") |
a6a7fe20 JS |
64 | if self.dom.create() < 0: |
65 | self.log.error("Could not start VM") | |
66 | raise BaseException | |
67 | ||
68 | def shutdown(self): | |
69 | if self.is_running(): | |
5499746c | 70 | self.log.info("Shutting down virtual machine") |
a6a7fe20 JS |
71 | if self.dom.shutdown() < 0: |
72 | self.log.error("Could not shutdown VM") | |
73 | raise BaseException | |
74 | else: | |
5499746c | 75 | self.log.warn("Cannot shutdown a not running domain") |
a6a7fe20 JS |
76 | |
77 | def undefine(self): | |
5499746c JS |
78 | # We cannot undefine a not defined dom object |
79 | if self.dom != None: | |
80 | self.log.info("Undefining virtual machine") | |
81 | self.dom.undefine() | |
82 | else: | |
83 | self.log.warn("Cannot undefine a not defined domain") | |
a6a7fe20 JS |
84 | |
85 | def create_snapshot(self): | |
5499746c | 86 | self.log.info("Creating snapshot of virtual machine") |
a6a7fe20 JS |
87 | self.snapshot = self.dom.snapshotCreateXML(self.snapshot_xml) |
88 | ||
5499746c | 89 | if self.snapshot == None: |
a6a7fe20 JS |
90 | self.log.error("Could not create snapshot") |
91 | raise BaseException | |
92 | ||
93 | def revert_snapshot(self): | |
5499746c JS |
94 | if self.snapshot != None: |
95 | self.log.info("Reverting snapshot") | |
96 | self.dom.revertToSnapshot(self.snapshot) | |
97 | self.log.info("Deleting snapshot") | |
98 | self.snapshot.delete() | |
99 | else: | |
100 | self.log.warn("No active snapshot. Cannot revert and delete snapshot") | |
a6a7fe20 JS |
101 | |
102 | def is_running(self): | |
5499746c JS |
103 | # Only if we have a valid dom object we can check the dom state |
104 | if self.dom == None: | |
105 | return False | |
a6a7fe20 JS |
106 | |
107 | state, reason = self.dom.state() | |
108 | ||
109 | if state == libvirt.VIR_DOMAIN_RUNNING: | |
110 | return True | |
111 | else: | |
112 | return False | |
113 | ||
114 | def get_serial_device(self): | |
115 | ||
116 | if not self.is_running(): | |
117 | raise BaseException | |
118 | ||
119 | xml_root = ET.fromstring(self.dom.XMLDesc(0)) | |
120 | ||
121 | elem = xml_root.find("./devices/serial/source") | |
122 | return elem.get("path") | |
123 | ||
341e7373 JS |
124 | def get_name(self): |
125 | xml_root = ET.fromstring(self.vm_xml) | |
126 | ||
127 | elem = xml_root.find("./name") | |
128 | return elem.text | |
129 | ||
a6a7fe20 | 130 | def check_is_booted_up(self): |
ee227ea1 | 131 | serial_con = serial_connection.Serial_connection(self.get_serial_device()) |
a6a7fe20 JS |
132 | |
133 | serial_con.write("\n") | |
134 | # This will block till the domain is booted up | |
135 | serial_con.read(1) | |
136 | ||
137 | #serial_con.close() | |
138 | ||
fc35cba1 | 139 | def login(self, log_file, log_start_time=None, longest_machine_name=10): |
a6a7fe20 | 140 | try: |
ee227ea1 | 141 | self.serial_con = serial_connection.Serial_connection(self.get_serial_device(), |
6c352a80 JS |
142 | username=self.username, |
143 | log_file=log_file, | |
144 | log_start_time=log_start_time, | |
fc35cba1 JS |
145 | name=self.name, |
146 | longest_machine_name=longest_machine_name) | |
a6a7fe20 JS |
147 | self.serial_con.login(self.password) |
148 | except BaseException as e: | |
149 | self.log.error("Could not connect to the domain via serial console") | |
61bf2ba3 JS |
150 | self.log.exception(e) |
151 | raise e | |
a6a7fe20 JS |
152 | |
153 | def cmd(self, cmd): | |
154 | return self.serial_con.command(cmd) | |
155 | ||
156 | def copy_in(self, fr, to): | |
157 | try: | |
158 | self.disk.mount(self.root_uid, "/") | |
159 | self.disk.copy_in(fr, to) | |
160 | except BaseException as e: | |
161 | self.log.error(e) | |
162 | finally: | |
163 | self.disk.umount("/") | |
6632e137 | 164 | self.disk.close() |