]>
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 | ||
25572214 | 22 | self.serial_con = None |
5499746c | 23 | |
a6a7fe20 JS |
24 | try: |
25 | with open(vm_xml_file) as fobj: | |
26 | self.vm_xml = fobj.read() | |
27 | except FileNotFoundError as error: | |
341e7373 JS |
28 | logger.error("No such file: {}".format(vm_xml_file)) |
29 | ||
30 | try: | |
31 | self.name = self.get_name() | |
32 | except BaseException as error: | |
33 | logger.error("Could not get name of the machine: {}".format(vm_xml_file)) | |
34 | raise error | |
35 | ||
36 | self.log = logger.getChild(self.name) | |
37 | self.log.debug("Name of this machine is {}".format(self.name)) | |
a6a7fe20 JS |
38 | |
39 | try: | |
40 | with open(snapshot_xml_file) as fobj: | |
41 | self.snapshot_xml = fobj.read() | |
42 | except FileNotFoundError as error: | |
43 | self.log.error("No such file: {}".format(snapshot_xml_file)) | |
44 | ||
45 | self.image = image | |
46 | ||
47 | if not os.path.isfile(self.image): | |
48 | self.log.error("No such file: {}".format(self.image)) | |
49 | ||
50 | self.root_uid = root_uid | |
ee227ea1 | 51 | self.disk = disk.Disk(image) |
a6a7fe20 JS |
52 | |
53 | self.username = username | |
54 | self.password = password | |
55 | ||
56 | def define(self): | |
5499746c | 57 | self.log.info("Defining virtual machine") |
8b744f18 | 58 | self.dom = self.con.defineXML(self.vm_xml) |
a6a7fe20 JS |
59 | if self.dom == None: |
60 | self.log.error("Could not define VM") | |
61 | raise BaseException | |
62 | ||
63 | def start(self): | |
5499746c | 64 | self.log.info("Starting virtual machine") |
a6a7fe20 JS |
65 | if self.dom.create() < 0: |
66 | self.log.error("Could not start VM") | |
67 | raise BaseException | |
68 | ||
69 | def shutdown(self): | |
70 | if self.is_running(): | |
5499746c | 71 | self.log.info("Shutting down virtual machine") |
a6a7fe20 JS |
72 | if self.dom.shutdown() < 0: |
73 | self.log.error("Could not shutdown VM") | |
74 | raise BaseException | |
75 | else: | |
5499746c | 76 | self.log.warn("Cannot shutdown a not running domain") |
a6a7fe20 JS |
77 | |
78 | def undefine(self): | |
5499746c JS |
79 | # We cannot undefine a not defined dom object |
80 | if self.dom != None: | |
81 | self.log.info("Undefining virtual machine") | |
82 | self.dom.undefine() | |
83 | else: | |
84 | self.log.warn("Cannot undefine a not defined domain") | |
a6a7fe20 JS |
85 | |
86 | def create_snapshot(self): | |
5499746c | 87 | self.log.info("Creating snapshot of virtual machine") |
a6a7fe20 JS |
88 | self.snapshot = self.dom.snapshotCreateXML(self.snapshot_xml) |
89 | ||
5499746c | 90 | if self.snapshot == None: |
a6a7fe20 JS |
91 | self.log.error("Could not create snapshot") |
92 | raise BaseException | |
93 | ||
94 | def revert_snapshot(self): | |
5499746c JS |
95 | if self.snapshot != None: |
96 | self.log.info("Reverting snapshot") | |
97 | self.dom.revertToSnapshot(self.snapshot) | |
98 | self.log.info("Deleting snapshot") | |
99 | self.snapshot.delete() | |
100 | else: | |
101 | self.log.warn("No active snapshot. Cannot revert and delete snapshot") | |
a6a7fe20 JS |
102 | |
103 | def is_running(self): | |
5499746c JS |
104 | # Only if we have a valid dom object we can check the dom state |
105 | if self.dom == None: | |
106 | return False | |
a6a7fe20 JS |
107 | |
108 | state, reason = self.dom.state() | |
109 | ||
110 | if state == libvirt.VIR_DOMAIN_RUNNING: | |
111 | return True | |
112 | else: | |
113 | return False | |
114 | ||
115 | def get_serial_device(self): | |
116 | ||
117 | if not self.is_running(): | |
118 | raise BaseException | |
119 | ||
120 | xml_root = ET.fromstring(self.dom.XMLDesc(0)) | |
121 | ||
122 | elem = xml_root.find("./devices/serial/source") | |
123 | return elem.get("path") | |
124 | ||
341e7373 JS |
125 | def get_name(self): |
126 | xml_root = ET.fromstring(self.vm_xml) | |
127 | ||
128 | elem = xml_root.find("./name") | |
129 | return elem.text | |
130 | ||
a6a7fe20 | 131 | def check_is_booted_up(self): |
d9f6c37f | 132 | serial_con = serial_connection.SerialConnection(self.get_serial_device()) |
a6a7fe20 JS |
133 | |
134 | serial_con.write("\n") | |
135 | # This will block till the domain is booted up | |
136 | serial_con.read(1) | |
137 | ||
138 | #serial_con.close() | |
139 | ||
25572214 JS |
140 | # This function should initialize the serial connection |
141 | def serial_init(self, log_file=None, log_start_time=None, longest_machine_name=10): | |
a6a7fe20 | 142 | try: |
d9f6c37f | 143 | self.serial_con = serial_connection.SerialConnection(self.get_serial_device(), |
25572214 JS |
144 | username=self.username, |
145 | password= self.password, | |
146 | log_file=log_file, | |
147 | log_start_time=log_start_time, | |
148 | name=self.name, | |
149 | longest_machine_name=longest_machine_name) | |
150 | except BaseException as e: | |
151 | self.log.error("Could initialize the serial console") | |
152 | self.log.exception(e) | |
153 | raise e | |
154 | ||
155 | ||
156 | # This function should create a ready to use serial connection for this serial domain | |
157 | def serial_connect(self): | |
158 | try: | |
159 | # Do the real connect | |
160 | self.serial_con.connect() | |
a6a7fe20 JS |
161 | except BaseException as e: |
162 | self.log.error("Could not connect to the domain via serial console") | |
61bf2ba3 JS |
163 | self.log.exception(e) |
164 | raise e | |
a6a7fe20 | 165 | |
25572214 JS |
166 | def serial_disconnect(self): |
167 | try: | |
168 | self.serial_con.disconnect() | |
169 | except BaseException as e: | |
170 | self.log.error("Could not disconnect from the serial console") | |
171 | self.log.exception(e) | |
172 | raise e | |
173 | ||
a6a7fe20 JS |
174 | def cmd(self, cmd): |
175 | return self.serial_con.command(cmd) | |
176 | ||
177 | def copy_in(self, fr, to): | |
178 | try: | |
056b7a3a JS |
179 | self.disk.inspect() |
180 | self.disk.mount() | |
a6a7fe20 JS |
181 | self.disk.copy_in(fr, to) |
182 | except BaseException as e: | |
183 | self.log.error(e) | |
184 | finally: | |
056b7a3a | 185 | self.disk.umount() |
6632e137 | 186 | self.disk.close() |