LGTM. Thanks, Jose
On Thu, Feb 06, 2014 at 05:24:13PM +0100, Santi Raffa wrote: > This updates objects, constructors and mocks for Instance and Cluster > objects in Python and Haskell. > > Signed-off-by: Santi Raffa <[email protected]> > --- > lib/bootstrap.py | 2 + > lib/luxi.py | 4 +- > lib/objects.py | 142 > ++++++++++++++++++++++++------ > src/Ganeti/Config.hs | 2 +- > src/Ganeti/HTools/Program/Harep.hs | 4 + > src/Ganeti/Objects.hs | 109 ++++++++++++----------- > test/data/instance-prim-sec.txt | 1 + > test/hs/Test/Ganeti/Objects.hs | 2 + > test/hs/Test/Ganeti/Query/Instance.hs | 2 +- > test/py/cmdlib/testsupport/config_mock.py | 5 ++ > test/py/ganeti.config_unittest.py | 4 +- > test/py/ganeti.ovf_unittest.py | 2 + > 12 files changed, 197 insertions(+), 82 deletions(-) > > diff --git a/lib/bootstrap.py b/lib/bootstrap.py > index 772134e..2169563 100644 > --- a/lib/bootstrap.py > +++ b/lib/bootstrap.py > @@ -786,6 +786,8 @@ def InitCluster(cluster_name, mac_prefix, # pylint: > disable=R0913, R0914 > disk_state_static=disk_state, > enabled_disk_templates=enabled_disk_templates, > candidate_certs=candidate_certs, > + osparams={}, > + osparams_private_cluster={} > ) > master_node_config = objects.Node(name=hostname.name, > primary_ip=hostname.ip, > diff --git a/lib/luxi.py b/lib/luxi.py > index 5c69f46..654a7bc 100644 > --- a/lib/luxi.py > +++ b/lib/luxi.py > @@ -95,7 +95,9 @@ class Client(cl.AbstractClient): > return self.CallMethod(REQ_PICKUP_JOB, (job,)) > > def SubmitJob(self, ops): > - ops_state = map(lambda op: op.__getstate__(), ops) > + ops_state = map(lambda op: op.__getstate__() > + if not isinstance(op, objects.ConfigObject) > + else op.ToDict(_with_private=True), ops) > return self.CallMethod(REQ_SUBMIT_JOB, (ops_state, )) > > def SubmitJobToDrainedQueue(self, ops): > diff --git a/lib/objects.py b/lib/objects.py > index 2537c39..2f3c005 100644 > --- a/lib/objects.py > +++ b/lib/objects.py > @@ -47,6 +47,7 @@ from ganeti import constants > from ganeti import netutils > from ganeti import outils > from ganeti import utils > +from ganeti import serializer > > from socket import AF_INET > > @@ -213,7 +214,7 @@ class ConfigObject(outils.ValidatedSlots): > > """ > > - def ToDict(self): > + def ToDict(self, _with_private=False): > """Convert to a dict holding only standard python types. > > The generic routine just dumps all of this object's attributes in > @@ -222,6 +223,15 @@ class ConfigObject(outils.ValidatedSlots): > which case the object should subclass the function in order to > make sure all objects returned are only standard python types. > > + Private fields can be included or not with the _with_private switch. > + The actual implementation of this switch is left for those subclassses > + with private fields to implement. > + > + @type _with_private: bool > + @param _with_private: if True, the object will leak its private fields in > + the dictionary representation. If False, the values > + will be replaced with None. > + > """ > result = {} > for name in self.GetAllSlots(): > @@ -333,13 +343,13 @@ class TaggableObject(ConfigObject): > except KeyError: > raise errors.TagError("Tag not found") > > - def ToDict(self): > + def ToDict(self, _with_private=False): > """Taggable-object-specific conversion to standard python types. > > This replaces the tags set with a list. > > """ > - bo = super(TaggableObject, self).ToDict() > + bo = super(TaggableObject, self).ToDict(_with_private=_with_private) > > tags = bo.get("tags", None) > if isinstance(tags, set): > @@ -388,14 +398,14 @@ class ConfigData(ConfigObject): > "serial_no", > ] + _TIMESTAMPS > > - def ToDict(self): > + def ToDict(self, _with_private=False): > """Custom function for top-level config data. > > This just replaces the list of instances, nodes and the cluster > with standard python types. > > """ > - mydict = super(ConfigData, self).ToDict() > + mydict = super(ConfigData, self).ToDict(_with_private=_with_private) > mydict["cluster"] = mydict["cluster"].ToDict() > for key in "nodes", "instances", "nodegroups", "networks": > mydict[key] = outils.ContainerToDicts(mydict[key]) > @@ -746,7 +756,8 @@ class Disk(ConfigObject): > self.dynamic_params = dyn_disk_params > > # pylint: disable=W0221 > - def ToDict(self, include_dynamic_params=False): > + def ToDict(self, include_dynamic_params=False, > + _with_private=False): > """Disk-specific conversion to standard python types. > > This replaces the children lists of objects with lists of > @@ -1063,6 +1074,7 @@ class Instance(TaggableObject): > "hvparams", > "beparams", > "osparams", > + "osparams_private", > "admin_state", > "nics", > "disks", > @@ -1187,14 +1199,17 @@ class Instance(TaggableObject): > " 0 to %d" % (idx, len(self.disks) - 1), > errors.ECODE_INVAL) > > - def ToDict(self): > + def ToDict(self, _with_private=False): > """Instance-specific conversion to standard python types. > > This replaces the children lists of objects with lists of standard > python types. > > """ > - bo = super(Instance, self).ToDict() > + bo = super(Instance, self).ToDict(_with_private=_with_private) > + > + if _with_private: > + bo["osparams_private"] = self.osparams_private.Unprivate() > > for attr in "nics", "disks": > alist = bo.get(attr, None) > @@ -1238,6 +1253,8 @@ class Instance(TaggableObject): > pass > if self.osparams is None: > self.osparams = {} > + if self.osparams_private is None: > + self.osparams_private = serializer.PrivateDict() > UpgradeBeParams(self.beparams) > if self.disks_active is None: > self.disks_active = self.admin_state == constants.ADMINST_UP > @@ -1407,11 +1424,11 @@ class Node(TaggableObject): > if self.powered is None: > self.powered = True > > - def ToDict(self): > + def ToDict(self, _with_private=False): > """Custom function for serializing. > > """ > - data = super(Node, self).ToDict() > + data = super(Node, self).ToDict(_with_private=_with_private) > > hv_state = data.get("hv_state", None) > if hv_state is not None: > @@ -1459,14 +1476,14 @@ class NodeGroup(TaggableObject): > "networks", > ] + _TIMESTAMPS + _UUID > > - def ToDict(self): > + def ToDict(self, _with_private=False): > """Custom function for nodegroup. > > This discards the members object, which gets recalculated and is only > kept > in memory. > > """ > - mydict = super(NodeGroup, self).ToDict() > + mydict = super(NodeGroup, self).ToDict(_with_private=_with_private) > del mydict["members"] > return mydict > > @@ -1559,6 +1576,7 @@ class Cluster(TaggableObject): > "os_hvp", > "beparams", > "osparams", > + "osparams_private_cluster", > "nicparams", > "ndparams", > "diskparams", > @@ -1600,9 +1618,11 @@ class Cluster(TaggableObject): > if self.os_hvp is None: > self.os_hvp = {} > > - # osparams added before 2.2 > if self.osparams is None: > self.osparams = {} > + # osparams_private_cluster added in 2.12 > + if self.osparams_private_cluster is None: > + self.osparams_private_cluster = {} > > self.ndparams = UpgradeNDParams(self.ndparams) > > @@ -1719,11 +1739,17 @@ class Cluster(TaggableObject): > """ > return self.enabled_hypervisors[0] > > - def ToDict(self): > + def ToDict(self, _with_private=False): > """Custom function for cluster. > > """ > - mydict = super(Cluster, self).ToDict() > + mydict = super(Cluster, self).ToDict(_with_private=_with_private) > + > + # Explicitly save private parameters. > + if _with_private: > + for os in mydict["osparams_private_cluster"]: > + mydict["osparams_private_cluster"][os] = \ > + self.osparams_private_cluster[os].Unprivate() > > if self.tcpudp_port_pool is None: > tcpudp_port_pool = [] > @@ -1855,25 +1881,89 @@ class Cluster(TaggableObject): > """ > return FillDict(self.nicparams.get(constants.PP_DEFAULT, {}), nicparams) > > - def SimpleFillOS(self, os_name, os_params): > + def SimpleFillOS(self, os_name, > + os_params_public, > + os_params_private=None, > + os_params_secret=None): > """Fill an instance's osparams dict with cluster defaults. > > @type os_name: string > @param os_name: the OS name to use > - @type os_params: dict > - @param os_params: the dict to fill with default values > + @type os_params_public: dict > + @param os_params_public: the dict to fill with default values > + @type os_params_private: dict > + @param os_params_private: the dict with private fields to fill > + with default values. Not passing this field > + results in no private fields being added to the > + return value. Private fields will be wrapped in > + L{Private} objects. > + @type os_params_secret: dict > + @param os_params_secret: the dict with secret fields to fill > + with default values. Not passing this field > + results in no secret fields being added to the > + return value. Private fields will be wrapped in > + L{Private} objects. > @rtype: dict > @return: a copy of the instance's osparams with missing keys filled from > - the cluster defaults > + the cluster defaults. Private and secret parameters are not included > + unless the respective optional parameters are supplied. > > """ > name_only = os_name.split("+", 1)[0] > - # base OS > - result = self.osparams.get(name_only, {}) > - # OS with variant > - result = FillDict(result, self.osparams.get(os_name, {})) > - # specified params > - return FillDict(result, os_params) > + > + defaults_base_public = self.osparams.get(name_only, {}) > + defaults_public = FillDict(defaults_base_public, > + self.osparams.get(os_name, {})) > + params_public = FillDict(defaults_public, os_params_public) > + > + if os_params_private is not None: > + defaults_base_private = self.osparams_private_cluster.get(name_only, > {}) > + defaults_private = FillDict(defaults_base_private, > + self.osparams_private_cluster.get(os_name, > + {})) > + params_private = FillDict(defaults_private, os_params_private) > + else: > + params_private = {} > + > + if os_params_secret is not None: > + # There can't be default secret settings, so there's nothing to be > done. > + params_secret = os_params_secret > + else: > + params_secret = {} > + > + # Enforce that the set of keys be distinct: > + duplicate_keys = utils.GetRepeatedKeys(params_public, > + params_private, > + params_secret) > + if not duplicate_keys: > + > + # Actually update them: > + params_public.update(params_private) > + params_public.update(params_secret) > + > + return params_public > + > + else: > + > + def formatter(keys): > + return utils.CommaJoin(sorted(map(repr, keys))) if keys else "(none)" > + > + #Lose the values. > + params_public = set(params_public) > + params_private = set(params_private) > + params_secret = set(params_secret) > + > + msg = """Cannot assign multiple values to OS parameters. > + > + Conflicting OS parameters that would have been set by this operation: > + - at public visibility: {public} > + - at private visibility: {private} > + - at secret visibility: {secret} > + """.format(dupes=formatter(duplicate_keys), > + public=formatter(params_public & duplicate_keys), > + private=formatter(params_private & duplicate_keys), > + secret=formatter(params_secret & duplicate_keys)) > + raise errors.OpPrereqError(msg) > > @staticmethod > def SimpleFillHvState(hv_state): > @@ -2061,7 +2151,7 @@ class _QueryResponseBase(ConfigObject): > "fields", > ] > > - def ToDict(self): > + def ToDict(self, _with_private=False): > """Custom function for serializing. > > """ > diff --git a/src/Ganeti/Config.hs b/src/Ganeti/Config.hs > index 069a03b..c35ba5b 100644 > --- a/src/Ganeti/Config.hs > +++ b/src/Ganeti/Config.hs > @@ -276,7 +276,7 @@ getFilledInstBeParams cfg inst = do > return $ fillBeParams parentParams (instBeparams inst) > > -- | Retrieves the instance os params, missing values filled with cluster > --- defaults. > +-- defaults. This does NOT include private and secret parameters. > getFilledInstOsParams :: ConfigData -> Instance -> OsParams > getFilledInstOsParams cfg inst = > let osLookupName = takeWhile (/= '+') (instOs inst) > diff --git a/src/Ganeti/HTools/Program/Harep.hs > b/src/Ganeti/HTools/Program/Harep.hs > index 0bd2104..933410d 100644 > --- a/src/Ganeti/HTools/Program/Harep.hs > +++ b/src/Ganeti/HTools/Program/Harep.hs > @@ -307,6 +307,8 @@ detectBroken nl inst = > , opInstanceUuid = Nothing > , opOsType = Nothing > , opTempOsParams = Nothing > + , opOsparamsPrivate = Nothing > + , opOsparamsSecret = Nothing > , opForceVariant = False > } > ]) > @@ -359,6 +361,8 @@ detectBroken nl inst = > , opInstanceUuid = Nothing > , opOsType = Nothing > , opTempOsParams = Nothing > + , opOsparamsPrivate = Nothing > + , opOsparamsSecret = Nothing > , opForceVariant = False > } > ]) > diff --git a/src/Ganeti/Objects.hs b/src/Ganeti/Objects.hs > index d0eba27..68fb0b2 100644 > --- a/src/Ganeti/Objects.hs > +++ b/src/Ganeti/Objects.hs > @@ -31,6 +31,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, > MA > module Ganeti.Objects > ( HvParams > , OsParams > + , OsParamsPrivate > , PartialNicParams(..) > , FilledNicParams(..) > , fillNicParams > @@ -123,6 +124,7 @@ type HvParams = Container JSValue > -- container, since the keys are dynamically declared by the OSes, and > -- the values are always strings. > type OsParams = Container String > +type OsParamsPrivate = Container (Private String) > > -- | Class of objects that have timestamps. > class TimeStampObject a where > @@ -440,18 +442,19 @@ $(buildParam "Be" "bep" > ]) > > $(buildObject "Instance" "inst" $ > - [ simpleField "name" [t| String |] > - , simpleField "primary_node" [t| String |] > - , simpleField "os" [t| String |] > - , simpleField "hypervisor" [t| Hypervisor |] > - , simpleField "hvparams" [t| HvParams |] > - , simpleField "beparams" [t| PartialBeParams |] > - , simpleField "osparams" [t| OsParams |] > - , simpleField "admin_state" [t| AdminState |] > - , simpleField "nics" [t| [PartialNic] |] > - , simpleField "disks" [t| [Disk] |] > - , simpleField "disk_template" [t| DiskTemplate |] > - , simpleField "disks_active" [t| Bool |] > + [ simpleField "name" [t| String |] > + , simpleField "primary_node" [t| String |] > + , simpleField "os" [t| String |] > + , simpleField "hypervisor" [t| Hypervisor |] > + , simpleField "hvparams" [t| HvParams |] > + , simpleField "beparams" [t| PartialBeParams |] > + , simpleField "osparams" [t| OsParams |] > + , simpleField "osparams_private" [t| OsParamsPrivate |] > + , simpleField "admin_state" [t| AdminState |] > + , simpleField "nics" [t| [PartialNic] |] > + , simpleField "disks" [t| [Disk] |] > + , simpleField "disk_template" [t| DiskTemplate |] > + , simpleField "disks_active" [t| Bool |] > , optionalField $ simpleField "network_port" [t| Int |] > ] > ++ timeStampFields > @@ -650,6 +653,7 @@ type ClusterBeParams = Container FilledBeParams > > -- | Cluster OsParams. > type ClusterOsParams = Container OsParams > +type ClusterOsParamsPrivate = Container (Private OsParams) > > -- | Cluster NicParams. > type ClusterNicParams = Container FilledNicParams > @@ -665,49 +669,50 @@ type CandidateCertificates = Container String > > -- * Cluster definitions > $(buildObject "Cluster" "cluster" $ > - [ simpleField "rsahostkeypub" [t| String |] > + [ simpleField "rsahostkeypub" [t| String |] > , optionalField $ > - simpleField "dsahostkeypub" [t| String |] > - , simpleField "highest_used_port" [t| Int |] > - , simpleField "tcpudp_port_pool" [t| [Int] |] > - , simpleField "mac_prefix" [t| String |] > + simpleField "dsahostkeypub" [t| String |] > + , simpleField "highest_used_port" [t| Int |] > + , simpleField "tcpudp_port_pool" [t| [Int] |] > + , simpleField "mac_prefix" [t| String |] > , optionalField $ > - simpleField "volume_group_name" [t| String |] > - , simpleField "reserved_lvs" [t| [String] |] > + simpleField "volume_group_name" [t| String |] > + , simpleField "reserved_lvs" [t| [String] |] > , optionalField $ > - simpleField "drbd_usermode_helper" [t| String |] > - , simpleField "master_node" [t| String |] > - , simpleField "master_ip" [t| String |] > - , simpleField "master_netdev" [t| String |] > - , simpleField "master_netmask" [t| Int |] > - , simpleField "use_external_mip_script" [t| Bool |] > - , simpleField "cluster_name" [t| String |] > - , simpleField "file_storage_dir" [t| String |] > - , simpleField "shared_file_storage_dir" [t| String |] > - , simpleField "gluster_storage_dir" [t| String |] > - , simpleField "enabled_hypervisors" [t| [Hypervisor] |] > - , simpleField "hvparams" [t| ClusterHvParams |] > - , simpleField "os_hvp" [t| OsHvParams |] > - , simpleField "beparams" [t| ClusterBeParams |] > - , simpleField "osparams" [t| ClusterOsParams |] > - , simpleField "nicparams" [t| ClusterNicParams |] > - , simpleField "ndparams" [t| FilledNDParams |] > - , simpleField "diskparams" [t| DiskParams |] > - , simpleField "candidate_pool_size" [t| Int |] > - , simpleField "modify_etc_hosts" [t| Bool |] > - , simpleField "modify_ssh_setup" [t| Bool |] > - , simpleField "maintain_node_health" [t| Bool |] > - , simpleField "uid_pool" [t| UidPool |] > - , simpleField "default_iallocator" [t| String |] > - , simpleField "default_iallocator_params" [t| IAllocatorParams |] > - , simpleField "hidden_os" [t| [String] |] > - , simpleField "blacklisted_os" [t| [String] |] > - , simpleField "primary_ip_family" [t| IpFamily |] > - , simpleField "prealloc_wipe_disks" [t| Bool |] > - , simpleField "ipolicy" [t| FilledIPolicy |] > - , simpleField "enabled_disk_templates" [t| [DiskTemplate] |] > - , simpleField "candidate_certs" [t| CandidateCertificates |] > - , simpleField "max_running_jobs" [t| Int |] > + simpleField "drbd_usermode_helper" [t| String |] > + , simpleField "master_node" [t| String |] > + , simpleField "master_ip" [t| String |] > + , simpleField "master_netdev" [t| String |] > + , simpleField "master_netmask" [t| Int |] > + , simpleField "use_external_mip_script" [t| Bool |] > + , simpleField "cluster_name" [t| String |] > + , simpleField "file_storage_dir" [t| String |] > + , simpleField "shared_file_storage_dir" [t| String |] > + , simpleField "gluster_storage_dir" [t| String |] > + , simpleField "enabled_hypervisors" [t| [Hypervisor] |] > + , simpleField "hvparams" [t| ClusterHvParams |] > + , simpleField "os_hvp" [t| OsHvParams |] > + , simpleField "beparams" [t| ClusterBeParams |] > + , simpleField "osparams" [t| ClusterOsParams |] > + , simpleField "osparams_private_cluster" [t| ClusterOsParamsPrivate |] > + , simpleField "nicparams" [t| ClusterNicParams |] > + , simpleField "ndparams" [t| FilledNDParams |] > + , simpleField "diskparams" [t| DiskParams |] > + , simpleField "candidate_pool_size" [t| Int |] > + , simpleField "modify_etc_hosts" [t| Bool |] > + , simpleField "modify_ssh_setup" [t| Bool |] > + , simpleField "maintain_node_health" [t| Bool |] > + , simpleField "uid_pool" [t| UidPool |] > + , simpleField "default_iallocator" [t| String |] > + , simpleField "default_iallocator_params" [t| IAllocatorParams |] > + , simpleField "hidden_os" [t| [String] |] > + , simpleField "blacklisted_os" [t| [String] |] > + , simpleField "primary_ip_family" [t| IpFamily |] > + , simpleField "prealloc_wipe_disks" [t| Bool |] > + , simpleField "ipolicy" [t| FilledIPolicy |] > + , simpleField "enabled_disk_templates" [t| [DiskTemplate] |] > + , simpleField "candidate_certs" [t| CandidateCertificates |] > + , simpleField "max_running_jobs" [t| Int |] > ] > ++ timeStampFields > ++ uuidFields > diff --git a/test/data/instance-prim-sec.txt b/test/data/instance-prim-sec.txt > index a947dfb..b159439 100644 > --- a/test/data/instance-prim-sec.txt > +++ b/test/data/instance-prim-sec.txt > @@ -72,6 +72,7 @@ > ], > "os": "busybox", > "osparams": {}, > + "osparams_private": {}, > "primary_node": "60e687a0-21fc-4577-997f-ccd08925fa65", > "serial_no": 2, > "uuid": "aec390cb-5eae-44e6-bcc2-ec14d31347f0" > diff --git a/test/hs/Test/Ganeti/Objects.hs b/test/hs/Test/Ganeti/Objects.hs > index a07a95b..511fe4e 100644 > --- a/test/hs/Test/Ganeti/Objects.hs > +++ b/test/hs/Test/Ganeti/Objects.hs > @@ -123,6 +123,8 @@ instance Arbitrary Instance where > <*> arbitrary > -- osparams > <*> pure (GenericContainer Map.empty) > + -- osparams_private > + <*> pure (GenericContainer Map.empty) > -- admin_state > <*> arbitrary > -- nics > diff --git a/test/hs/Test/Ganeti/Query/Instance.hs > b/test/hs/Test/Ganeti/Query/Instance.hs > index 4c1e7c2..ffd3ea6 100644 > --- a/test/hs/Test/Ganeti/Query/Instance.hs > +++ b/test/hs/Test/Ganeti/Query/Instance.hs > @@ -52,7 +52,7 @@ createInstance name pnodeUuid adminState = > Instance name pnodeUuid "" Kvm > (GenericContainer Map.empty) > (PartialBeParams Nothing Nothing Nothing Nothing Nothing Nothing) > - (GenericContainer Map.empty) > + (GenericContainer Map.empty) (GenericContainer Map.empty) > adminState [] [] DTDrbd8 False Nothing epochTime epochTime "" 0 Set.empty > where epochTime = TOD 0 0 > > diff --git a/test/py/cmdlib/testsupport/config_mock.py > b/test/py/cmdlib/testsupport/config_mock.py > index d485453..669a8e4 100644 > --- a/test/py/cmdlib/testsupport/config_mock.py > +++ b/test/py/cmdlib/testsupport/config_mock.py > @@ -181,6 +181,7 @@ class ConfigMock(config.ConfigWriter): > hvparams=None, > beparams=None, > osparams=None, > + osparams_private=None, > admin_state=None, > nics=None, > disks=None, > @@ -217,6 +218,8 @@ class ConfigMock(config.ConfigWriter): > beparams = {} > if osparams is None: > osparams = {} > + if osparams_private is None: > + osparams_private = {} > if admin_state is None: > admin_state = constants.ADMINST_DOWN > if nics is None: > @@ -247,6 +250,7 @@ class ConfigMock(config.ConfigWriter): > hvparams=hvparams, > beparams=beparams, > osparams=osparams, > + osparams_private=osparams_private, > admin_state=admin_state, > nics=nics, > disks=disks, > @@ -571,6 +575,7 @@ class ConfigMock(config.ConfigWriter): > os_hvp={self.GetDefaultOs().name: constants.HVC_DEFAULTS.copy()}, > beparams=None, > osparams=None, > + osparams_private_cluster=None, > nicparams={constants.PP_DEFAULT: constants.NICC_DEFAULTS}, > ndparams=None, > diskparams=None, > diff --git a/test/py/ganeti.config_unittest.py > b/test/py/ganeti.config_unittest.py > index 55918cc..5f341a4 100755 > --- a/test/py/ganeti.config_unittest.py > +++ b/test/py/ganeti.config_unittest.py > @@ -35,6 +35,7 @@ from ganeti import objects > from ganeti import utils > from ganeti import netutils > from ganeti import compat > +from ganeti import serializer > from ganeti.cmdlib import instance > > from ganeti.config import TemporaryReservationManager > @@ -109,7 +110,8 @@ class TestConfigRunner(unittest.TestCase): > uuid="test-uuid", > disks=[], nics=[], > disk_template=constants.DT_DISKLESS, > - primary_node=self._get_object().GetMasterNode()) > + primary_node=self._get_object().GetMasterNode(), > + osparams_private=serializer.PrivateDict()) > return inst > > def testEmpty(self): > diff --git a/test/py/ganeti.ovf_unittest.py b/test/py/ganeti.ovf_unittest.py > index bc72496..6e2ce40 100755 > --- a/test/py/ganeti.ovf_unittest.py > +++ b/test/py/ganeti.ovf_unittest.py > @@ -173,6 +173,7 @@ ARGS_VBOX = dict(ARGS_EXPORT_DIR, **{ > "os": "lenny-image", > "hypervisor": ("xen-pvm", {}), > "osparams":{}, > + "osparams_private":{}, > "disks": [], > }) > ARGS_COMPLETE = dict(ARGS_VBOX, **{ > @@ -188,6 +189,7 @@ ARGS_BROKEN = dict(ARGS_EXPORT_DIR , **{ > "name": "test-instance", > "os": "lenny-image", > "osparams": {}, > + "osparams_private":{}, > }) > > EXP_ARGS_COMPRESSED = dict(ARGS_EXPORT_DIR, **{ > -- > 1.9.0.rc1.175.g0b1dcb5 > -- Jose Antonio Lopes Ganeti Engineering Google Germany GmbH Dienerstr. 12, 80331, München Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Graham Law, Christine Elizabeth Flores Steuernummer: 48/725/00206 Umsatzsteueridentifikationsnummer: DE813741370
