Skip to content

Commit 8a5d633

Browse files
authored
Merge pull request #4 from cloudify-incubator/net_backup
Add network/disk backups
2 parents 49afb52 + 13047a0 commit 8a5d633

18 files changed

Lines changed: 985 additions & 123 deletions

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Description for VM
3737

3838
**Supported properties:**
3939
* `libvirt_auth`: connection url, by default: `qemu:///system`
40+
* `backup_dir`: directory for save backups, by default: `./`
4041

4142
**Inputs for actions:**
4243
* `configure`:
@@ -52,6 +53,7 @@ Description for Network
5253

5354
**Supported properties:**
5455
* `libvirt_auth`: connection url, by default: `qemu:///system`
56+
* `backup_dir`: directory for save backups, by default: `./`
5557

5658
**Inputs for actions:**
5759
* `create`:

cloudify_libvirt/common.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
15+
import os
1516
from cloudify import ctx
17+
from cloudify import exceptions as cfy_exc
1618

1719

1820
def get_libvirt_params(**kwargs):
@@ -30,3 +32,41 @@ def get_libvirt_params(**kwargs):
3032
template_params.update(kwargs.get('params', {}))
3133
ctx.instance.runtime_properties['params'] = template_params
3234
return libvirt_auth, template_params
35+
36+
37+
def get_backupname(kwargs):
38+
if not kwargs.get("snapshot_name"):
39+
raise cfy_exc.NonRecoverableError(
40+
'Backup name must be provided.'
41+
)
42+
return kwargs["snapshot_name"]
43+
44+
45+
def get_backupdir(kwargs):
46+
return "{}/{}".format(
47+
ctx.node.properties.get('backup_dir', "."),
48+
kwargs["snapshot_name"].replace("/", "_")
49+
)
50+
51+
52+
def save_node_state(backup_dir, object_name, content):
53+
# save object state as string
54+
if not os.path.isdir(backup_dir):
55+
os.makedirs(backup_dir)
56+
with open("{}/{}.xml".format(backup_dir, object_name), 'w') as file:
57+
file.write(content)
58+
59+
60+
def read_node_state(backup_dir, object_name):
61+
# read object state as string
62+
if not os.path.isfile("{}/{}.xml".format(backup_dir, object_name)):
63+
return None
64+
with open("{}/{}.xml".format(backup_dir, object_name), 'r') as file:
65+
return file.read()
66+
67+
68+
def delete_node_state(backup_dir, object_name):
69+
# read object state as string
70+
if not os.path.isfile("{}/{}.xml".format(backup_dir, object_name)):
71+
return
72+
os.remove("{}/{}.xml".format(backup_dir, object_name))

cloudify_libvirt/domain_tasks.py

Lines changed: 109 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@
2222
from cloudify.decorators import operation
2323
from cloudify import exceptions as cfy_exc
2424
from pkg_resources import resource_filename
25-
from cloudify_libvirt.common import get_libvirt_params
25+
import cloudify_libvirt.common as common
2626

2727

2828
@operation
2929
def create(**kwargs):
3030
ctx.logger.info("create")
31-
get_libvirt_params(**kwargs)
31+
common.get_libvirt_params(**kwargs)
3232
# dont need to run anything, we attach disc's in preconfigure state
3333
# so we will define domain later
3434

@@ -37,7 +37,7 @@ def create(**kwargs):
3737
def configure(**kwargs):
3838
ctx.logger.info("configure")
3939

40-
libvirt_auth, template_params = get_libvirt_params(**kwargs)
40+
libvirt_auth, template_params = common.get_libvirt_params(**kwargs)
4141
conn = libvirt.open(libvirt_auth)
4242
if conn is None:
4343
raise cfy_exc.NonRecoverableError(
@@ -105,7 +105,7 @@ def start(**kwargs):
105105
ctx.logger.info("No servers for start")
106106
return
107107

108-
libvirt_auth, _ = get_libvirt_params(**kwargs)
108+
libvirt_auth, _ = common.get_libvirt_params(**kwargs)
109109
conn = libvirt.open(libvirt_auth)
110110
if conn is None:
111111
raise cfy_exc.NonRecoverableError(
@@ -152,7 +152,7 @@ def stop(**kwargs):
152152
ctx.logger.info("No servers for delete")
153153
return
154154

155-
libvirt_auth, _ = get_libvirt_params(**kwargs)
155+
libvirt_auth, _ = common.get_libvirt_params(**kwargs)
156156
conn = libvirt.open(libvirt_auth)
157157
if conn is None:
158158
raise cfy_exc.NonRecoverableError(
@@ -198,7 +198,7 @@ def resume(**kwargs):
198198
ctx.logger.info("No servers for resume")
199199
return
200200

201-
libvirt_auth, _ = get_libvirt_params(**kwargs)
201+
libvirt_auth, _ = common.get_libvirt_params(**kwargs)
202202
conn = libvirt.open(libvirt_auth)
203203
if conn is None:
204204
raise cfy_exc.NonRecoverableError(
@@ -244,7 +244,7 @@ def suspend(**kwargs):
244244
ctx.logger.info("No servers for suspend")
245245
return
246246

247-
libvirt_auth, _ = get_libvirt_params(**kwargs)
247+
libvirt_auth, _ = common.get_libvirt_params(**kwargs)
248248
conn = libvirt.open(libvirt_auth)
249249
if conn is None:
250250
raise cfy_exc.NonRecoverableError(
@@ -312,7 +312,7 @@ def delete(**kwargs):
312312
ctx.logger.info("No servers for delete")
313313
return
314314

315-
libvirt_auth, _ = get_libvirt_params(**kwargs)
315+
libvirt_auth, _ = common.get_libvirt_params(**kwargs)
316316
conn = libvirt.open(libvirt_auth)
317317
if conn is None:
318318
raise cfy_exc.NonRecoverableError(
@@ -360,14 +360,6 @@ def delete(**kwargs):
360360
conn.close()
361361

362362

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-
371363
@operation
372364
def snapshot_create(**kwargs):
373365
ctx.logger.info("backup")
@@ -378,44 +370,15 @@ def snapshot_create(**kwargs):
378370
ctx.logger.info("No servers for backup.")
379371
return
380372

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
373+
snapshot_name = common.get_backupname(kwargs)
385374

386-
libvirt_auth, template_params = get_libvirt_params(**kwargs)
375+
libvirt_auth, template_params = common.get_libvirt_params(**kwargs)
387376
conn = libvirt.open(libvirt_auth)
388377
if conn is None:
389378
raise cfy_exc.NonRecoverableError(
390379
'Failed to open connection to the hypervisor'
391380
)
392381

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-
419382
try:
420383
try:
421384
dom = conn.lookupByName(resource_id)
@@ -427,16 +390,59 @@ def snapshot_create(**kwargs):
427390
raise cfy_exc.NonRecoverableError(
428391
'Failed to find the domain'
429392
)
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()))
393+
394+
if kwargs.get("snapshot_incremental"):
395+
backup_file = kwargs.get('backup_file')
396+
backup_template = kwargs.get('backup_template')
397+
snapshot_type = kwargs.get('snapshot_type')
398+
399+
if backup_file:
400+
backup_template = ctx.get_resource(backup_file)
401+
402+
if not backup_file and not backup_template:
403+
resource_dir = resource_filename(__name__, 'templates')
404+
backup_file = '{}/snapshot.xml'.format(resource_dir)
405+
ctx.logger.info("Will be used internal: %s" % backup_file)
406+
407+
if not backup_template:
408+
domain_desc = open(backup_file)
409+
with domain_desc:
410+
backup_template = domain_desc.read()
411+
412+
template_engine = Template(backup_template)
413+
if not template_params:
414+
template_params = {}
415+
416+
params = {
417+
"ctx": ctx,
418+
'snapshot_name': snapshot_name,
419+
'snapshot_description': snapshot_type
420+
}
421+
params.update(template_params)
422+
xmlconfig = template_engine.render(params)
423+
424+
ctx.logger.debug(repr(xmlconfig))
425+
426+
try:
427+
# will raise exception if unexist
428+
snapshot = dom.snapshotLookupByName(snapshot_name)
429+
raise cfy_exc.NonRecoverableError(
430+
"Snapshot {snapshot_name} already exists."
431+
.format(snapshot_name=snapshot.getName(),))
432+
except libvirt.libvirtError:
433+
pass
434+
snapshot = dom.snapshotCreateXML(xmlconfig)
435+
ctx.logger.info("Snapshot name: {}".format(snapshot.getName()))
436+
else:
437+
if common.read_node_state(common.get_backupdir(kwargs),
438+
resource_id):
439+
raise cfy_exc.NonRecoverableError(
440+
"Backup {snapshot_name} already exists."
441+
.format(snapshot_name=snapshot_name,))
442+
common.save_node_state(common.get_backupdir(kwargs), resource_id,
443+
dom.XMLDesc())
444+
ctx.logger.info("Backup {snapshot_name} is created."
445+
.format(snapshot_name=snapshot_name,))
440446
finally:
441447
conn.close()
442448

@@ -450,12 +456,9 @@ def snapshot_delete(**kwargs):
450456
ctx.logger.info("No servers for remove_backup.")
451457
return
452458

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
459+
snapshot_name = common.get_backupname(kwargs)
457460

458-
libvirt_auth, template_params = get_libvirt_params(**kwargs)
461+
libvirt_auth, template_params = common.get_libvirt_params(**kwargs)
459462
conn = libvirt.open(libvirt_auth)
460463
if conn is None:
461464
raise cfy_exc.NonRecoverableError(
@@ -474,18 +477,26 @@ def snapshot_delete(**kwargs):
474477
'Failed to find the domain'
475478
)
476479

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()
480+
if kwargs.get("snapshot_incremental"):
481+
# raised exception if libvirt has not found any
482+
snapshot = dom.snapshotLookupByName(snapshot_name)
483+
if snapshot.numChildren():
484+
subsnapshots = [
485+
snap.getName() for snap in snapshot.listAllChildren()
486+
]
487+
raise cfy_exc.NonRecoverableError(
488+
"Sub snapshots {subsnapshots} found for {snapshot_name}. "
489+
"You should remove subsnaphots before remove current."
490+
.format(snapshot_name=snapshot_name,
491+
subsnapshots=repr(subsnapshots)))
492+
snapshot.delete()
493+
else:
494+
if not common.read_node_state(common.get_backupdir(kwargs),
495+
resource_id):
496+
raise cfy_exc.NonRecoverableError(
497+
"No backups found with name: {snapshot_name}."
498+
.format(snapshot_name=snapshot_name,))
499+
common.delete_node_state(common.get_backupdir(kwargs), resource_id)
489500
ctx.logger.info("Backup deleted: {}".format(snapshot_name))
490501
finally:
491502
conn.close()
@@ -500,12 +511,9 @@ def snapshot_apply(**kwargs):
500511
ctx.logger.info("No servers for restore.")
501512
return
502513

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
514+
snapshot_name = common.get_backupname(kwargs)
507515

508-
libvirt_auth, template_params = get_libvirt_params(**kwargs)
516+
libvirt_auth, template_params = common.get_libvirt_params(**kwargs)
509517
conn = libvirt.open(libvirt_auth)
510518
if conn is None:
511519
raise cfy_exc.NonRecoverableError(
@@ -524,10 +532,27 @@ def snapshot_apply(**kwargs):
524532
'Failed to find the domain'
525533
)
526534

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()))
535+
if kwargs.get("snapshot_incremental"):
536+
# raised exception if libvirt has not found any
537+
snapshot = dom.snapshotLookupByName(snapshot_name)
538+
dom.revertToSnapshot(snapshot)
539+
ctx.logger.info("Reverted to: {}".format(snapshot.getName()))
540+
else:
541+
dom_backup = common.read_node_state(common.get_backupdir(kwargs),
542+
resource_id)
543+
if not dom_backup:
544+
raise cfy_exc.NonRecoverableError(
545+
"No backups found with name: {snapshot_name}."
546+
.format(snapshot_name=snapshot_name,))
547+
548+
if dom_backup.strip() != dom.XMLDesc().strip():
549+
ctx.logger.info("We have different configs,\n{}\nvs\n{}\n"
550+
.format(
551+
repr(dom_backup.strip()),
552+
repr(dom.XMLDesc().strip())))
553+
else:
554+
ctx.logger.info("Already used such configuration: {}"
555+
.format(snapshot_name))
531556
finally:
532557
conn.close()
533558

@@ -553,7 +578,7 @@ def perfomance(**kwargs):
553578
ctx.logger.info("No servers for statistics.")
554579
return
555580

556-
libvirt_auth, template_params = get_libvirt_params(**kwargs)
581+
libvirt_auth, template_params = common.get_libvirt_params(**kwargs)
557582
conn = libvirt.open(libvirt_auth)
558583
if conn is None:
559584
raise cfy_exc.NonRecoverableError(

0 commit comments

Comments
 (0)