machine: Improve log messages and stop process of a machine
[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
23         try:
24             with open(vm_xml_file) as fobj:
25                 self.vm_xml = fobj.read()
26         except FileNotFoundError as error:
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))
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
50         self.disk = disk.disk(image)
51
52         self.username = username
53         self.password = password
54
55     def define(self):
56         self.log.info("Defining virtual machine")
57         self.dom = self.con.defineXML(self.vm_xml)
58         if self.dom == None:
59             self.log.error("Could not define VM")
60             raise BaseException
61
62     def start(self):
63         self.log.info("Starting virtual machine")
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():
70             self.log.info("Shutting down virtual machine")
71             if self.dom.shutdown() < 0:
72                 self.log.error("Could not shutdown VM")
73                 raise BaseException
74         else:
75             self.log.warn("Cannot shutdown a not running domain")
76
77     def undefine(self):
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")
84
85     def create_snapshot(self):
86         self.log.info("Creating snapshot of virtual machine")
87         self.snapshot = self.dom.snapshotCreateXML(self.snapshot_xml)
88
89         if self.snapshot == None:
90             self.log.error("Could not create snapshot")
91             raise BaseException
92
93     def revert_snapshot(self):
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")
101
102     def is_running(self):
103         # Only if we have a valid dom object we can check the dom state
104         if self.dom == None:
105             return False
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
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
130     def check_is_booted_up(self):
131         serial_con = serial_connection.serial_connection(self.get_serial_device())
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
139     def login(self, log_file, log_start_time=None, longest_machine_name=10):
140         try:
141             self.serial_con = serial_connection.serial_connection(self.get_serial_device(),
142                                 username=self.username,
143                                 log_file=log_file,
144                                 log_start_time=log_start_time,
145                                 name=self.name,
146                                 longest_machine_name=longest_machine_name)
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")
150             self.log.exception(e)
151             raise e
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("/")
164             self.disk.close()