diff --git a/bootstrapvz/base/bootstrapinfo.py b/bootstrapvz/base/bootstrapinfo.py index 70dfa4d..0039253 100644 --- a/bootstrapvz/base/bootstrapinfo.py +++ b/bootstrapvz/base/bootstrapinfo.py @@ -130,10 +130,10 @@ class BootstrapInformation(object): def __getstate__(self): state = self.__dict__.copy() - exclude_keys = ['volume', 'source_lists', 'preference_lists', 'packages'] + exclude_keys = ['source_lists', 'preference_lists', 'packages'] for key in exclude_keys: del state[key] - state['__class__'] = 'bootstrapvz.base.bootstrapinfo.BootstrapInformation' + state['__class__'] = self.__module__ + '.' + self.__class__.__name__ return state def __setstate__(self, state): diff --git a/bootstrapvz/base/fs/partitionmaps/none.py b/bootstrapvz/base/fs/partitionmaps/none.py index d9b122a..439fe48 100644 --- a/bootstrapvz/base/fs/partitionmaps/none.py +++ b/bootstrapvz/base/fs/partitionmaps/none.py @@ -32,3 +32,12 @@ class NoPartitions(object): :rtype: Bytes """ return self.root.get_end() + + def __getstate__(self): + state = self.__dict__.copy() + state['__class__'] = self.__module__ + '.' + self.__class__.__name__ + return state + + def __setstate__(self, state): + for key in state: + self.__dict__[key] = state[key] diff --git a/bootstrapvz/base/manifest.py b/bootstrapvz/base/manifest.py index 27e7686..02ddae8 100644 --- a/bootstrapvz/base/manifest.py +++ b/bootstrapvz/base/manifest.py @@ -133,7 +133,7 @@ class Manifest(object): raise ManifestError(message, self.path, data_path) def __getstate__(self): - return {'__class__': 'bootstrapvz.base.manifest.Manifest', + return {'__class__': self.__module__ + '.' + self.__class__.__name__, 'path': self.path, 'data': self.data} diff --git a/bootstrapvz/common/bytes.py b/bootstrapvz/common/bytes.py index cd6d9c4..a64e0ec 100644 --- a/bootstrapvz/common/bytes.py +++ b/bootstrapvz/common/bytes.py @@ -126,6 +126,14 @@ class Bytes(object): self.qty %= other return self + def __getstate__(self): + return {'__class__': self.__module__ + '.' + self.__class__.__name__, + 'qty': self.qty, + } + + def __setstate__(self, state): + self.qty = state['qty'] + class UnitError(Exception): pass diff --git a/bootstrapvz/common/fs/qemuvolume.py b/bootstrapvz/common/fs/qemuvolume.py index 2f9adfe..26d3300 100644 --- a/bootstrapvz/common/fs/qemuvolume.py +++ b/bootstrapvz/common/fs/qemuvolume.py @@ -77,3 +77,7 @@ class QEMUVolume(LoopbackVolume): if not self._is_nbd_used(device_name): return os.path.join('/dev', device_name) raise VolumeError('Unable to find free nbd device.') + + def __setstate__(self, state): + for key in state: + self.__dict__[key] = state[key] diff --git a/bootstrapvz/common/fsm_proxy.py b/bootstrapvz/common/fsm_proxy.py index bf02c73..a968b92 100644 --- a/bootstrapvz/common/fsm_proxy.py +++ b/bootstrapvz/common/fsm_proxy.py @@ -43,6 +43,19 @@ class FSMProxy(object): if not hasattr(self, event): setattr(self, event, make_proxy(fsm, event)) + def __getstate__(self): + state = {} + for key, value in self.__dict__.iteritems(): + if callable(value) or key == 'fsm': + continue + state[key] = value + state['__class__'] = self.__module__ + '.' + self.__class__.__name__ + return state + + def __setstate__(self, state): + for key in state: + self.__dict__[key] = state[key] + class FSMProxyError(Exception): pass diff --git a/bootstrapvz/remote/__init__.py b/bootstrapvz/remote/__init__.py index a7ec9da..4aae205 100644 --- a/bootstrapvz/remote/__init__.py +++ b/bootstrapvz/remote/__init__.py @@ -1,45 +1,83 @@ """Remote module containing methods to bootstrap remotely """ -import bootstrapvz.common.exceptions +from Pyro4.util import SerializerBase +import logging +log = logging.getLogger(__name__) + +supported_classes = ['bootstrapvz.base.manifest.Manifest', + 'bootstrapvz.base.bootstrapinfo.BootstrapInformation', + 'bootstrapvz.common.fs.loopbackvolume.LoopbackVolume', + 'bootstrapvz.common.fs.qemuvolume.QEMUVolume', + 'bootstrapvz.common.fs.virtualdiskimage.VirtualDiskImage', + 'bootstrapvz.common.fs.virtualmachinedisk.VirtualMachineDisk', + 'bootstrapvz.base.fs.partitionmaps.gpt.GPTPartitionMap', + 'bootstrapvz.base.fs.partitionmaps.msdos.MSDOSPartitionMap', + 'bootstrapvz.base.fs.partitionmaps.none.NoPartitions', + 'bootstrapvz.base.fs.partitions.gpt.GPTPartition', + 'bootstrapvz.base.fs.partitions.gpt_swap.GPTSwapPartition', + 'bootstrapvz.base.fs.partitions.msdos.MSDOSPartition', + 'bootstrapvz.base.fs.partitions.msdos_swap.MSDOSSwapPartition', + 'bootstrapvz.base.fs.partitions.single.SinglePartition', + 'bootstrapvz.base.fs.partitions.unformatted.UnformattedPartition', + 'bootstrapvz.common.bytes.Bytes', + ] + +supported_exceptions = ['bootstrapvz.common.exceptions.ManifestError', + 'bootstrapvz.common.exceptions.TaskListError', + 'bootstrapvz.common.exceptions.TaskError', + 'bootstrapvz.base.fs.exceptions.VolumeError', + 'bootstrapvz.base.fs.exceptions.PartitionError', + 'bootstrapvz.base.pkg.exceptions.PackageError', + 'bootstrapvz.base.pkg.exceptions.SourceError', + 'bootstrapvz.common.bytes.UnitError', + 'bootstrapvz.common.fsm_proxy.FSMProxyError', + ] def register_deserialization_handlers(): - from Pyro4.util import SerializerBase - SerializerBase.register_dict_to_class('bootstrapvz.base.manifest.Manifest', deserialize_manifest) - SerializerBase.register_dict_to_class('bootstrapvz.base.bootstrapinfo.BootstrapInformation', deserialize_bootstrapinfo) - SerializerBase.register_dict_to_class('bootstrapvz.common.exceptions.ManifestError', deserialize_exception) - SerializerBase.register_dict_to_class('bootstrapvz.common.exceptions.TaskListError', deserialize_exception) - SerializerBase.register_dict_to_class('bootstrapvz.common.exceptions.TaskError', deserialize_exception) + for supported_class in supported_classes: + SerializerBase.register_dict_to_class(supported_class, deserialize) + for supported_exc in supported_exceptions: + SerializerBase.register_dict_to_class(supported_exc, deserialize_exception) def unregister_deserialization_handlers(): + for supported_class in supported_classes: + SerializerBase.unregister_dict_to_class(supported_class, deserialize) + for supported_exc in supported_exceptions: + SerializerBase.unregister_dict_to_class(supported_exc, deserialize_exception) + + +def deserialize_exception(fq_classname, data): + class_object = get_class_object(fq_classname) from Pyro4.util import SerializerBase - SerializerBase.unregister_dict_to_class('bootstrapvz.base.manifest.Manifest') - SerializerBase.unregister_dict_to_class('bootstrapvz.base.bootstrapinfo.BootstrapInformation') - SerializerBase.unregister_dict_to_class('bootstrapvz.common.exceptions.ManifestError') - SerializerBase.unregister_dict_to_class('bootstrapvz.common.exceptions.TaskListError') - SerializerBase.unregister_dict_to_class('bootstrapvz.common.exceptions.TaskError') + return SerializerBase.make_exception(class_object, data) -def deserialize_manifest(classname, state): - from bootstrapvz.base.manifest import Manifest - manifest = Manifest.__new__(Manifest) - manifest.__setstate__(state) - return manifest +def deserialize(fq_classname, data): + class_object = get_class_object(fq_classname) + from Pyro4.util import SerpentSerializer + from Pyro4.errors import SecurityError + ser = SerpentSerializer() + state = {} + for key, value in data.items(): + try: + state[key] = ser.recreate_classes(value) + except SecurityError as e: + msg = 'Unable to deserialize key `{key}\' on {class_name}'.format(key=key, class_name=fq_classname) + import pprint + msg += pprint.pformat(data) + raise Exception(msg, e) + + instance = class_object.__new__(class_object) + instance.__setstate__(state) + return instance -def deserialize_bootstrapinfo(classname, state): - from bootstrapvz.base.bootstrapinfo import BootstrapInformation - bootstrap_info = BootstrapInformation.__new__(BootstrapInformation) - bootstrap_info.__setstate__(state) - return bootstrap_info - -deserialize_map = {'bootstrapvz.common.exceptions.ManifestError': bootstrapvz.common.exceptions.ManifestError, - 'bootstrapvz.common.exceptions.TaskListError': bootstrapvz.common.exceptions.TaskListError, - 'bootstrapvz.common.exceptions.TaskError': bootstrapvz.common.exceptions.TaskError, - } - - -def deserialize_exception(classname, data): - from Pyro4.util import SerializerBase - return SerializerBase.make_exception(deserialize_map[classname], data) +def get_class_object(fq_classname): + parts = fq_classname.split('.') + module_name = '.'.join(parts[:-1]) + class_name = parts[-1] + import importlib + imported_module = importlib.import_module(module_name) + return getattr(imported_module, class_name) diff --git a/tests/integration/virtualbox_tests.py b/tests/integration/virtualbox_tests.py index 08b5a24..caf39e9 100644 --- a/tests/integration/virtualbox_tests.py +++ b/tests/integration/virtualbox_tests.py @@ -38,6 +38,7 @@ volume: os.close(handle) build_server.download(bootstrap_info.volume.image_path, image_path) build_server.delete(bootstrap_info.volume.image_path) + # image_path = '/Users/anders/Workspace/cloud/images/debian-wheezy-amd64-141130.vmdk' try: image = tools.images.VirtualBoxImage(manifest, image_path) @@ -46,11 +47,9 @@ volume: instance.create() try: instance.boot() + # tools.reachable_with_ssh(instance) finally: instance.shutdown() - - # tools.test(instance) - finally: if 'instance' in locals(): instance.destroy()