]> git.ipfire.org Git - nitsi.git/blob - src/nitsi/machine.py
Rework the connection handling of the serial console
[nitsi.git] / src / nitsi / machine.py
1 #!/usr/bin/python3
2
3 import libvirt
4 import logging
5 import os
6 import xml.etree.ElementTree as ET
7
8 from . import disk
9 from . import serial_connection
10
11 logger = logging.getLogger("nitsi.machine")
12
13 class Machine():
14 def __init__(self, libvirt_con, vm_xml_file, snapshot_xml_file, image, root_uid, username, password):
15 self.log = logger.getChild(os.path.basename(vm_xml_file))
16 self.con = libvirt_con
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 self.serial_con = None
23
24 try:
25 with open(vm_xml_file) as fobj:
26 self.vm_xml = fobj.read()
27 except FileNotFoundError as error:
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))
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
51 self.disk = disk.Disk(image)
52
53 self.username = username
54 self.password = password
55
56 def define(self):
57 self.log.info("Defining virtual machine")
58 self.dom = self.con.defineXML(self.vm_xml)
59 if self.dom == None:
60 self.log.error("Could not define VM")
61 raise BaseException
62
63 def start(self):
64 self.log.info("Starting virtual machine")
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():
71 self.log.info("Shutting down virtual machine")
72 if self.dom.shutdown() < 0:
73 self.log.error("Could not shutdown VM")
74 raise BaseException
75 else:
76 self.log.warn("Cannot shutdown a not running domain")
77
78 def undefine(self):
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")
85
86 def create_snapshot(self):
87 self.log.info("Creating snapshot of virtual machine")
88 self.snapshot = self.dom.snapshotCreateXML(self.snapshot_xml)
89
90 if self.snapshot == None:
91 self.log.error("Could not create snapshot")
92 raise BaseException
93
94 def revert_snapshot(self):
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")
102
103 def is_running(self):
104 # Only if we have a valid dom object we can check the dom state
105 if self.dom == None:
106 return False
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
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
131 def check_is_booted_up(self):
132 serial_con = serial_connection.SerialConnection(self.get_serial_device())
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
140 # This function should initialize the serial connection
141 def serial_init(self, log_file=None, log_start_time=None, longest_machine_name=10):
142 try:
143 self.serial_con = serial_connection.SerialConnection(self.get_serial_device(),
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()
161 except BaseException as e:
162 self.log.error("Could not connect to the domain via serial console")
163 self.log.exception(e)
164 raise e
165
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
174 def cmd(self, cmd):
175 return self.serial_con.command(cmd)
176
177 def copy_in(self, fr, to):
178 try:
179 self.disk.mount(self.root_uid, "/")
180 self.disk.copy_in(fr, to)
181 except BaseException as e:
182 self.log.error(e)
183 finally:
184 self.disk.umount("/")
185 self.disk.close()