11########
2- # Copyright (c) 2016 GigaSpaces Technologies Ltd. All rights reserved
2+ # Copyright (c) 2016-2018 GigaSpaces Technologies Ltd. All rights reserved
33#
44# Licensed under the Apache License, Version 2.0 (the "License");
55# you may not use this file except in compliance with the License.
99#
1010# Unless required by applicable law or agreed to in writing, software
1111# distributed under the License is distributed on an "AS IS" BASIS,
12- # * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13- # * See the License for the specific language governing permissions and
14- # * limitations under the License.
12+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ # See the License for the specific language governing permissions and
14+ # limitations under the License.
1515
1616import libvirt
1717import time
@@ -80,25 +80,19 @@ def configure(**kwargs):
8080 params .update (template_params )
8181 xmlconfig = template_engine .render (params )
8282
83- ctx .logger .info (repr (xmlconfig ))
83+ ctx .logger .debug (repr (xmlconfig ))
8484
85- dom = conn .defineXML (xmlconfig )
86- if dom is None :
87- raise cfy_exc .NonRecoverableError (
88- 'Failed to define a domain from an XML definition.'
89- )
90-
91- ctx .instance .runtime_properties ['resource_id' ] = dom .name ()
92-
93- if dom .create () < 0 :
94- raise cfy_exc .NonRecoverableError (
95- 'Can not boot guest domain.'
96- )
97- conn .close ()
85+ try :
86+ dom = conn .defineXML (xmlconfig )
87+ if dom is None :
88+ raise cfy_exc .NonRecoverableError (
89+ 'Failed to define a domain from an XML definition.'
90+ )
9891
99- ctx .logger .info ('Guest ' + dom .name () + ' has booted' )
100- ctx .instance .runtime_properties ['resource_id' ] = dom .name ()
101- ctx .instance .runtime_properties ['params' ] = template_params
92+ ctx .instance .runtime_properties ['resource_id' ] = dom .name ()
93+ ctx .instance .runtime_properties ['params' ] = template_params
94+ finally :
95+ conn .close ()
10296
10397
10498@operation
@@ -130,10 +124,9 @@ def start(**kwargs):
130124 'Failed to find the domain'
131125 )
132126
133- state , reason = dom .state ()
134- for i in xrange (10 ):
135- state , reason = dom .state ()
127+ state , _ = dom .state ()
136128
129+ for i in xrange (10 ):
137130 if state == libvirt .VIR_DOMAIN_RUNNING :
138131 ctx .logger .info ("Looks as running." )
139132 return
@@ -144,7 +137,7 @@ def start(**kwargs):
144137 'Can not start guest domain.'
145138 )
146139 time .sleep (30 )
147- state , reason = dom .state ()
140+ state , _ = dom .state ()
148141 finally :
149142 conn .close ()
150143
@@ -178,10 +171,8 @@ def stop(**kwargs):
178171 'Failed to find the domain'
179172 )
180173
181- state , reason = dom .state ()
174+ state , _ = dom .state ()
182175 for i in xrange (10 ):
183- state , reason = dom .state ()
184-
185176 if state != libvirt .VIR_DOMAIN_RUNNING :
186177 ctx .logger .info ("Looks as not run." )
187178 return
@@ -192,7 +183,7 @@ def stop(**kwargs):
192183 'Can not shutdown guest domain.'
193184 )
194185 time .sleep (30 )
195- state , reason = dom .state ()
186+ state , _ = dom .state ()
196187 finally :
197188 conn .close ()
198189
@@ -226,10 +217,8 @@ def resume(**kwargs):
226217 'Failed to find the domain'
227218 )
228219
229- state , reason = dom .state ()
220+ state , _ = dom .state ()
230221 for i in xrange (10 ):
231- state , reason = dom .state ()
232-
233222 if state == libvirt .VIR_DOMAIN_RUNNING :
234223 ctx .logger .info ("Looks as running." )
235224 return
@@ -240,7 +229,7 @@ def resume(**kwargs):
240229 'Can not suspend guest domain.'
241230 )
242231 time .sleep (30 )
243- state , reason = dom .state ()
232+ state , _ = dom .state ()
244233 finally :
245234 conn .close ()
246235
@@ -274,10 +263,8 @@ def suspend(**kwargs):
274263 'Failed to find the domain'
275264 )
276265
277- state , reason = dom .state ()
266+ state , _ = dom .state ()
278267 for i in xrange (10 ):
279- state , reason = dom .state ()
280-
281268 if state != libvirt .VIR_DOMAIN_RUNNING :
282269 ctx .logger .info ("Looks as not run." )
283270 return
@@ -288,11 +275,33 @@ def suspend(**kwargs):
288275 'Can not suspend guest domain.'
289276 )
290277 time .sleep (30 )
291- state , reason = dom .state ()
278+ state , _ = dom .state ()
292279 finally :
293280 conn .close ()
294281
295282
283+ def _cleanup_snapshots (ctx , dom ):
284+ snapshots = dom .listAllSnapshots ()
285+ snapshots_count = len (snapshots )
286+
287+ for _ in xrange (snapshots_count ):
288+ for snapshot in snapshots :
289+ # we can delete only snapshot without child
290+ if not snapshot .numChildren ():
291+ ctx .logger .info ("Remove {} snapshot."
292+ .format (snapshot .getName ()))
293+ snapshot .delete ()
294+ snapshots = dom .listAllSnapshots ()
295+
296+ if len (snapshots ):
297+ subsnapshots = [
298+ snap .getName () for snap in snapshots
299+ ]
300+ raise cfy_exc .RecoverableError (
301+ "Still have several snapshots: {subsnapshots}."
302+ .format (subsnapshots = repr (subsnapshots )))
303+
304+
296305@operation
297306def delete (** kwargs ):
298307 ctx .logger .info ("delete" )
@@ -322,25 +331,203 @@ def delete(**kwargs):
322331 'Failed to find the domain'
323332 )
324333
325- state , reason = dom .state ()
334+ if dom .snapshotNum ():
335+ ctx .logger .info ("Domain has {} snapshots."
336+ .format (dom .snapshotNum ()))
337+ _cleanup_snapshots (ctx , dom )
338+
339+ state , _ = dom .state ()
326340
327341 if state != libvirt .VIR_DOMAIN_SHUTOFF :
328342 if dom .destroy () < 0 :
329- raise cfy_exc .NonRecoverableError (
343+ raise cfy_exc .RecoverableError (
330344 'Can not destroy guest domain.'
331345 )
332346
333347 try :
334348 if dom .undefineFlags (libvirt .VIR_DOMAIN_UNDEFINE_NVRAM ) < 0 :
335- raise cfy_exc .NonRecoverableError (
349+ raise cfy_exc .RecoverableError (
336350 'Can not undefine guest domain with NVRAM.'
337351 )
338352 except AttributeError as e :
339353 ctx .logger .info ("Non critical error: {}" .format (str (e )))
340354 if dom .undefine () < 0 :
341- raise cfy_exc .NonRecoverableError (
355+ raise cfy_exc .RecoverableError (
342356 'Can not undefine guest domain.'
343357 )
358+ ctx .instance .runtime_properties ['resource_id' ] = None
359+ finally :
360+ conn .close ()
361+
362+
363+ def _get_backupname (kwargs ):
364+ if not kwargs .get ("snapshot_name" ):
365+ raise cfy_exc .NonRecoverableError (
366+ 'Backup name must be provided.'
367+ )
368+ return "vm-{}" .format (kwargs ["snapshot_name" ])
369+
370+
371+ @operation
372+ def snapshot_create (** kwargs ):
373+ ctx .logger .info ("backup" )
374+
375+ resource_id = ctx .instance .runtime_properties .get ('resource_id' )
376+
377+ if not resource_id :
378+ ctx .logger .info ("No servers for backup." )
379+ return
380+
381+ snapshot_name = _get_backupname (kwargs )
382+ if not kwargs .get ("snapshot_incremental" ):
383+ ctx .logger .info ("Create backup for VM is unsupported." )
384+ return
385+
386+ libvirt_auth , template_params = get_libvirt_params (** kwargs )
387+ conn = libvirt .open (libvirt_auth )
388+ if conn is None :
389+ raise cfy_exc .NonRecoverableError (
390+ 'Failed to open connection to the hypervisor'
391+ )
392+
393+ backup_file = kwargs .get ('backup_file' )
394+ backup_template = kwargs .get ('backup_template' )
395+
396+ if backup_file :
397+ backup_template = ctx .get_resource (backup_file )
398+
399+ if not backup_file and not backup_template :
400+ resource_dir = resource_filename (__name__ , 'templates' )
401+ backup_file = '{}/snapshot.xml' .format (resource_dir )
402+ ctx .logger .info ("Will be used internal: %s" % backup_file )
403+
404+ if not backup_template :
405+ domain_desc = open (backup_file )
406+ with domain_desc :
407+ backup_template = domain_desc .read ()
408+
409+ template_engine = Template (backup_template )
410+ if not template_params :
411+ template_params = {}
412+
413+ params = {"ctx" : ctx , 'snapshot_name' : snapshot_name }
414+ params .update (template_params )
415+ xmlconfig = template_engine .render (params )
416+
417+ ctx .logger .debug (repr (xmlconfig ))
418+
419+ try :
420+ try :
421+ dom = conn .lookupByName (resource_id )
422+ except Exception as e :
423+ dom = None
424+ ctx .logger .info ("Non critical error: {}" .format (str (e )))
425+
426+ if dom is None :
427+ raise cfy_exc .NonRecoverableError (
428+ 'Failed to find the domain'
429+ )
430+ try :
431+ # will raise exception if unexist
432+ snapshot = dom .snapshotLookupByName (snapshot_name )
433+ raise cfy_exc .NonRecoverableError (
434+ "Snapshot {snapshot_name} already exists."
435+ .format (snapshot_name = snapshot .getName (),))
436+ except libvirt .libvirtError :
437+ pass
438+ snapshot = dom .snapshotCreateXML (xmlconfig )
439+ ctx .logger .info ("Snapshot name: {}" .format (snapshot .getName ()))
440+ finally :
441+ conn .close ()
442+
443+
444+ @operation
445+ def snapshot_delete (** kwargs ):
446+ ctx .logger .info ("remove_backup" )
447+ resource_id = ctx .instance .runtime_properties .get ('resource_id' )
448+
449+ if not resource_id :
450+ ctx .logger .info ("No servers for remove_backup." )
451+ return
452+
453+ snapshot_name = _get_backupname (kwargs )
454+ if not kwargs .get ("snapshot_incremental" ):
455+ ctx .logger .info ("Delete backup for VM is unsupported." )
456+ return
457+
458+ libvirt_auth , template_params = get_libvirt_params (** kwargs )
459+ conn = libvirt .open (libvirt_auth )
460+ if conn is None :
461+ raise cfy_exc .NonRecoverableError (
462+ 'Failed to open connection to the hypervisor'
463+ )
464+
465+ try :
466+ try :
467+ dom = conn .lookupByName (resource_id )
468+ except Exception as e :
469+ dom = None
470+ ctx .logger .info ("Non critical error: {}" .format (str (e )))
471+
472+ if dom is None :
473+ raise cfy_exc .NonRecoverableError (
474+ 'Failed to find the domain'
475+ )
476+
477+ # raised exception if libvirt has not found any
478+ snapshot = dom .snapshotLookupByName (snapshot_name )
479+ if snapshot .numChildren ():
480+ subsnapshots = [
481+ snap .getName () for snap in snapshot .listAllChildren ()
482+ ]
483+ raise cfy_exc .NonRecoverableError (
484+ "Sub snapshots {subsnapshots} found for {snapshot_name}. "
485+ "You should remove subsnaphots before remove current."
486+ .format (snapshot_name = snapshot_name ,
487+ subsnapshots = repr (subsnapshots )))
488+ snapshot .delete ()
489+ ctx .logger .info ("Backup deleted: {}" .format (snapshot_name ))
490+ finally :
491+ conn .close ()
492+
493+
494+ @operation
495+ def snapshot_apply (** kwargs ):
496+ ctx .logger .info ("restore" )
497+ resource_id = ctx .instance .runtime_properties .get ('resource_id' )
498+
499+ if not resource_id :
500+ ctx .logger .info ("No servers for restore." )
501+ return
502+
503+ snapshot_name = _get_backupname (kwargs )
504+ if not kwargs .get ("snapshot_incremental" ):
505+ ctx .logger .info ("Restore from backup for VM is unsupported." )
506+ return
507+
508+ libvirt_auth , template_params = get_libvirt_params (** kwargs )
509+ conn = libvirt .open (libvirt_auth )
510+ if conn is None :
511+ raise cfy_exc .NonRecoverableError (
512+ 'Failed to open connection to the hypervisor'
513+ )
514+
515+ try :
516+ try :
517+ dom = conn .lookupByName (resource_id )
518+ except Exception as e :
519+ dom = None
520+ ctx .logger .info ("Non critical error: {}" .format (str (e )))
521+
522+ if dom is None :
523+ raise cfy_exc .NonRecoverableError (
524+ 'Failed to find the domain'
525+ )
526+
527+ # raised exception if libvirt has not found any
528+ snapshot = dom .snapshotLookupByName (snapshot_name )
529+ dom .revertToSnapshot (snapshot )
530+ ctx .logger .info ("Reverted to: {}" .format (snapshot .getName ()))
344531 finally :
345532 conn .close ()
346533
0 commit comments