]>
git.ipfire.org Git - nitsi.git/blob - test.py
0460d17d7f4ba0ec6cfebc5b69383ec345e8885e
11 import xml
.etree
.ElementTree
as ET
18 def __init__(self
, log_level
):
19 self
.log_level
= log_level
21 def debug(self
, string
):
22 if self
.log_level
>= 4:
23 print("DEBUG: {}".format(string
))
25 def error(self
, string
):
26 print("ERROR: {}".format(string
))
29 def __init__(self
, uri
):
32 self
.connection
= None
34 def get_domain_from_name(self
, name
):
35 dom
= self
.con
.lookupByName(name
)
43 if self
.connection
== None:
45 self
.connection
= libvirt
.open(self
.uri
)
46 except BaseException
as error
:
47 self
.log
.error("Could not connect to: {}".format(self
.uri
))
49 self
.log
.debug("Connected to: {}".format(self
.uri
))
50 return self
.connection
52 return self
.connection
56 def __init__(self
, vm_xml_file
, snapshot_xml_file
, image
, root_uid
):
58 self
.con
= libvirt_con("qemu:///system")
60 with
open(vm_xml_file
) as fobj
:
61 self
.vm_xml
= fobj
.read()
62 except FileNotFoundError
as error
:
63 self
.log
.error("No such file: {}".format(vm_xml_file
))
66 with
open(snapshot_xml_file
) as fobj
:
67 self
.snapshot_xml
= fobj
.read()
68 except FileNotFoundError
as error
:
69 self
.log
.error("No such file: {}".format(snapshot_xml_file
))
73 if not os
.path
.isfile(self
.image
):
74 self
.log
.error("No such file: {}".format(self
.image
))
76 self
.root_uid
= root_uid
79 self
.dom
= self
.con
.con
.defineXML(self
.vm_xml
)
81 self
.log
.error("Could not define VM")
85 if self
.dom
.create() < 0:
86 self
.log
.error("Could not start VM")
91 if self
.dom
.shutdown() < 0:
92 self
.log
.error("Could not shutdown VM")
95 self
.log
.error("Domain is not running")
100 def create_snapshot(self
):
102 self
.snapshot
= self
.dom
.snapshotCreateXML(self
.snapshot_xml
)
104 if not self
.snapshot
:
105 self
.log
.error("Could not create snapshot")
108 def revert_snapshot(self
):
109 self
.dom
.revertToSnapshot(self
.snapshot
)
110 self
.snapshot
.delete()
112 def is_running(self
):
114 state
, reason
= self
.dom
.state()
116 if state
== libvirt
.VIR_DOMAIN_RUNNING
:
121 def get_serial_device(self
):
123 if not self
.is_running():
126 xml_root
= ET
.fromstring(self
.dom
.XMLDesc(0))
128 elem
= xml_root
.find("./devices/serial/source")
129 return elem
.get("path")
131 def check_is_booted_up(self
):
132 serial_con
= connection(self
.get_serial_device())
134 serial_con
.write("\n")
135 # This will block till the domain is booted up
140 def login(self
, username
, password
):
142 self
.serial_con
= connection(self
.get_serial_device(), username
="root")
143 self
.serial_con
.login("25814@root")
144 except BaseException
as e
:
145 self
.log
.error("Could not connect to the domain via serial console")
148 return self
.serial_con
.command(cmd
)
152 def __init__(self
, device
, username
=None):
154 self
.back_at_prompt_pattern
= None
155 self
.username
= username
157 self
.con
= serial
.Serial(device
)
159 def read(self
, size
=1):
160 if len(self
.buffer) >= size
:
161 # throw away first size bytes in buffer
162 data
= self
.buffer[:size
]
163 # Set the buffer to the non used bytes
164 self
.buffer = self
.buffer[size
:]
168 # Set the size to the value we have to read now
169 size
= size
- len(self
.buffer)
170 # Set the buffer empty
172 return data
+ self
.con
.read(size
)
174 def peek(self
, size
=1):
175 if len(self
.buffer) <= size
:
176 self
.buffer += self
.con
.read(size
=size
- len(self
.buffer))
178 return self
.buffer[:size
]
181 self
.log
.debug(self
.buffer)
182 self
.buffer = self
.buffer + self
.con
.read(self
.con
.in_waiting
)
183 if b
"\n" in self
.buffer:
184 size
= self
.buffer.index(b
"\n") + 1
185 self
.log
.debug("We have a whole line in the buffer")
186 self
.log
.debug(self
.buffer)
187 self
.log
.debug("We split at {}".format(size
))
188 data
= self
.buffer[:size
]
189 self
.buffer = self
.buffer[size
:]
191 self
.log
.debug(self
.buffer)
196 return data
+ self
.con
.readline()
198 def back_at_prompt(self
):
203 # We need to use self.in_waiting because with self.con.in_waiting we get
204 # not the complete string
205 size
= len(self
.buffer) + self
.in_waiting
206 data
= self
.peek(size
)
209 if self
.back_at_prompt_pattern
== None:
210 #self.back_at_prompt_pattern = r"^\[{}@.+\]#".format(self.username)
211 self
.back_at_prompt_pattern
= re
.compile(r
"^\[{}@.+\]#".format(self
.username
), re
.MULTILINE
)
213 if self
.back_at_prompt_pattern
.search(data
.decode()):
218 def log_console_line(self
, line
):
219 self
.log
.debug("Get in function log_console_line()")
220 sys
.stdout
.write(line
)
223 def in_waiting(self
):
224 in_waiting_before
= 0
227 while in_waiting_before
!= self
.con
.in_waiting
:
228 in_waiting_before
= self
.con
.in_waiting
231 return self
.con
.in_waiting
234 def readline2(self
, pattern
=None):
238 pattern
= re
.compile(pattern
)
241 char
= self
.con
.read(1)
242 string
= string
+ char
.decode("utf-8")
243 string2
= string2
+ char
245 print(char
.decode("utf-8"), end
="")
248 if pattern
and pattern
.match(string
):
251 return {"string" : string
, "return-code" : 1}
257 return {"return-code" : 0}
259 def check_logged_in(self
, username
):
260 pattern
= "^\[" + username
+ "@.+\]#"
261 data
= self
.readline(pattern
=pattern
)
262 if data
["return-code"] == 1:
263 print("We are logged in")
266 print("We are not logged in")
269 def login(self
, password
):
270 if self
.username
== None:
271 self
.log
.error("Username cannot be blank")
274 # Hit enter to see what we get
275 self
.con
.write(b
'\n')
276 # We get two new lines \r\n ?
277 data
= self
.readline()
278 self
.log_console_line(data
.decode())
281 if self
.back_at_prompt():
282 self
.log
.debug("We are already logged in.")
285 # Read all line till we get login:
288 if not data
.decode() == "l":
289 self
.log
.debug("We get no l at the start")
290 self
.log_console_line(self
.readline().decode())
292 # We need to use self.in_waiting because with self.con.in_waiting we get
293 # not the complete string
294 size
= len(self
.buffer) + self
.in_waiting
295 data
= self
.peek(size
)
297 pattern
= r
"^.*login: "
298 pattern
= re
.compile(pattern
)
300 if pattern
.search(data
.decode()):
303 self
.log
.debug("The pattern does not match")
304 self
.log_console_line(self
.readline().decode())
307 string
= "{}\n".format(self
.username
)
308 self
.con
.write(string
.encode())
310 # read the login out of the buffer
311 data
= self
.readline()
312 self
.log
.debug("This is the login:{}".format(data
))
313 self
.log_console_line(data
.decode())
315 # We need to wait her till we get the full string "Password:"
316 #This is useless but self.in_waiting will wait the correct amount of time
317 size
= self
.in_waiting
319 string
= "{}\n".format(password
)
320 self
.con
.write(string
.encode())
322 # Print the 'Password:' line
323 data
= self
.readline()
324 self
.log_console_line(data
.decode())
326 while not self
.back_at_prompt():
327 # This will fail if the login failed so we need to look for the failed keyword
328 data
= self
.readline()
329 self
.log_console_line(data
.decode())
333 def write(self
, string
):
334 self
.log
.debug(string
)
335 self
.con
.write(string
.encode())
338 def command(self
, command
):
339 self
.write("{}\n".format(command
))
341 # We need to read out the prompt for this command first
342 # If we do not do this we will break the loop immediately
343 # because the prompt for this command is still in the buffer
344 data
= self
.readline()
345 self
.log_console_line(data
.decode())
347 while not self
.back_at_prompt():
348 data
= self
.readline()
349 self
.log_console_line(data
.decode())
352 # A class which define and undefine a virtual network based on an xml file
354 def __init__(self
, network_xml_file
):
356 self
.con
= libvirt_con("qemu:///system")
358 with
open(network_xml_file
) as fobj
:
359 self
.network_xml
= fobj
.read()
360 except FileNotFoundError
as error
:
361 self
.log
.error("No such file: {}".format(vm_xml_file
))
364 self
.network
= self
.con
.con
.networkDefineXML(self
.network_xml
)
367 self
.log
.error("Failed to define virtual network")
370 self
.network
.create()
373 self
.network
.destroy()
377 class RecipeExeption(Exception):
382 # Should read the test, check if the syntax are valid
383 # and return tuples with the ( host, command ) structure
385 def __init__(self
, path
):
387 self
.recipe_file
= path
390 if not os
.path
.isfile(self
.recipe_file
):
391 self
.log
.error("No such file: {}".format(self
.recipe_file
))
394 with
open(self
.recipe_file
) as fobj
:
395 self
.raw_recipe
= fobj
.readlines()
396 except FileNotFoundError
as error
:
397 self
.log
.error("No such file: {}".format(vm_xml_file
))
409 for line
in self
.raw_recipe
:
410 raw_line
= line
.split(":")
411 if len(raw_line
) < 2:
412 self
.log
.error("Error parsing the recipe in line {}".format(i
))
415 raw_line
= raw_line
[0].strip().split(" ")
416 if len(raw_line
) == 0:
417 self
.log
.error("Failed to parse the recipe in line {}".format(i
))
419 elif len(raw_line
) == 1:
420 if raw_line
[0] == "":
421 self
.log
.error("Failed to parse the recipe in line {}".format(i
))
423 machine
= raw_line
[0]
425 elif len(raw_line
) == 2:
426 machine
= raw_line
[0]
429 self
._recipe
.append((machine
.strip(), extra
.strip(), cmd
.strip()))
434 def __init__(self
, path
):
437 self
.path
= os
.path
.abspath(path
)
438 except BaseException
as e
:
439 self
.log
.error("Could not get absolute path")
441 self
.log
.debug(self
.path
)
443 self
.settings_file
= "{}/settings".format(self
.path
)
444 if not os
.path
.isfile(self
.settings_file
):
445 self
.log
.error("No such file: {}".format(self
.settings_file
))
447 self
.recipe_file
= "{}/recipe".format(self
.path
)
448 if not os
.path
.isfile(self
.recipe_file
):
449 self
.log
.error("No such file: {}".format(self
.recipe_file
))
451 def read_settings(self
):
452 self
.config
= configparser
.ConfigParser()
453 self
.config
.read(self
.settings_file
)
454 self
.name
= self
.config
["DEFAULT"]["Name"]
455 self
.description
= self
.config
["DEFAULT"]["Description"]
457 self
.virtual_environ_name
= self
.config
["VIRTUAL_ENVIRONMENT"]["Name"]
458 self
.virtual_environ_path
= self
.config
["VIRTUAL_ENVIRONMENT"]["Path"]
459 self
.virtual_environ_path
= os
.path
.normpath(self
.path
+ "/" + self
.virtual_environ_path
)
461 def virtual_environ_setup(self
):
462 self
.virtual_environ
= virtual_environ(self
.virtual_environ_path
)
464 self
.virtual_networks
= self
.virtual_environ
.get_networks()
466 self
.virtual_machines
= self
.virtual_environ
.get_machines()
468 def virtual_environ_start(self
):
471 def load_recipe(self
):
477 def virtual_environ_stop():
481 # Should return all vms and networks in a list
482 # and should provide the path to the necessary xml files
483 class virtual_environ():
484 def __init__(self
, path
):
487 self
.path
= os
.path
.abspath(path
)
488 except BaseException
as e
:
489 self
.log
.error("Could not get absolute path")
491 self
.log
.debug(self
.path
)
493 self
.settings_file
= "{}/settings".format(self
.path
)
494 if not os
.path
.isfile(self
.settings_file
):
495 self
.log
.error("No such file: {}".format(self
.settings_file
))
497 self
.log
.debug(self
.settings_file
)
498 self
.config
= configparser
.ConfigParser()
499 self
.config
.read(self
.settings_file
)
500 self
.name
= self
.config
["DEFAULT"]["name"]
501 self
.machines_string
= self
.config
["DEFAULT"]["machines"]
502 self
.networks_string
= self
.config
["DEFAULT"]["networks"]
505 for machine
in self
.machines_string
.split(","):
506 self
.machines
.append(machine
.strip())
509 for network
in self
.networks_string
.split(","):
510 self
.networks
.append(network
.strip())
512 self
.log
.debug(self
.machines
)
513 self
.log
.debug(self
.networks
)
515 def get_networks(self
):
517 for _network
in self
.networks
:
518 self
.log
.debug(_network
)
519 networks
.setdefault(_network
, network(os
.path
.normpath(self
.path
+ "/" + self
.config
[_network
]["xml_file"])))
522 def get_machines(self
):
524 for _machine
in self
.machines
:
525 self
.log
.debug(_machine
)
526 machines
.setdefault(_machine
, vm(
527 os
.path
.normpath(self
.path
+ "/" + self
.config
[_machine
]["xml_file"]),
528 os
.path
.normpath(self
.path
+ "/" + self
.config
[_machine
]["snapshot_xml_file"])))
533 if __name__
== "__main__":
536 parser
= argparse
.ArgumentParser()
538 parser
.add_argument("-d", "--directory", dest
="dir")
540 args
= parser
.parse_args()
542 _recipe
= recipe("/home/jonatan/python-testing-kvm/test/recipe")
543 currenttest
= test(args
.dir)
544 currenttest
.read_settings()
545 currenttest
.virtual_environ_setup()