Change in vdsm[master]: External hypervisor VMs integration

nsoffer at redhat.com nsoffer at redhat.com
Thu Oct 23 18:14:25 UTC 2014


Nir Soffer has posted comments on this change.

Change subject: External hypervisor VMs integration
......................................................................


Patch Set 14:

(12 comments)

I think this version is a regression.

http://gerrit.ovirt.org/#/c/33309/14/vdsm/v2v.py
File vdsm/v2v.py:

Line 34:               libvirt.VIR_DOMAIN_CRASHED: 'Crushed',
Line 35:               libvirt.VIR_DOMAIN_PMSUSPENDED: 'PMSuspended'}
Line 36: 
Line 37: 
Line 38: class V2V(object):
This is a bad name. Not only it repeats the module name "v2v.V2V", it also does not mean anything.

The simplest fix would be to eliminate this unneeded class - the previous version using module and some functions was better.
Line 39:     _instance = None
Line 40:     _instanceLock = threading.Lock()
Line 41: 
Line 42:     @classmethod


Line 43:     def getInstance(cls):
Line 44:         with cls._instanceLock:
Line 45:             if cls._instance is None:
Line 46:                 cls._instance = V2V()
Line 47:         return cls._instance
If you want a singleton, the best way to do it is to use a module, like you had before.

Even if you use classes, there is no need to expose this java-like mechanism to the outside world. Simply do:

    # v2v.py

    class _V2V:
        ...

    _instance = _V2V()

    def getExternalVMList(...):
        _instance.getExternalVMList()

Client code is much nicer now:

    # user.py

    import v2v

    v2v.getExternalVMList()

And we don't need any locks.

But in this case, this class is has no state, and it does not help anyone.
Line 48: 
Line 49:     def getExternalVMList(self, uri, username, password):
Line 50:         try:
Line 51:             conn = libvirtconnection.open_connection(uri=uri,


Line 57:                     root = ET.fromstring(vm.XMLDesc(0))
Line 58:                     params = {}
Line 59:                     params['vmName'] = vm.name()
Line 60:                     params['status'] = _VM_STATUS[vm.state()[0]]
Line 61:                     params.update(self._findGeneralInfo(root))
This would be better if you pass params to the functions and let them add what they want into the dict. Then we can rename them to _addGeneralInfo(root, params)

This way, there is single place where a key and its contents are generated.
Line 62:                     params.update(self._findDisks(root))
Line 63:                     params.update(self._findNetworks(root))
Line 64:                     ret.append(params)
Line 65:                 return ret


Line 59:                     params['vmName'] = vm.name()
Line 60:                     params['status'] = _VM_STATUS[vm.state()[0]]
Line 61:                     params.update(self._findGeneralInfo(root))
Line 62:                     params.update(self._findDisks(root))
Line 63:                     params.update(self._findNetworks(root))
We did not solve yet the fact that you add keys to this dict in two places, here and in the various findXXX methods. It will work if the functions return single value.
Line 64:                     ret.append(params)
Line 65:                 return ret
Line 66:         except libvirt.libvirtError as e:
Line 67:             raise V2VException(e.code, e.message)


Line 63:                     params.update(self._findNetworks(root))
Line 64:                     ret.append(params)
Line 65:                 return ret
Line 66:         except libvirt.libvirtError as e:
Line 67:             raise V2VException(e.code, e.message)
This looses the original stacktrace - not sure if it is useful, but bad idea in general.

If you cannot handle this error, let the caller handle it.
Line 68: 
Line 69:     def _memToMb(self, size, unit):
Line 70:         if unit == 'bytes' or unit == 'b':
Line 71:             return size / 1024 / 1024


Line 66:         except libvirt.libvirtError as e:
Line 67:             raise V2VException(e.code, e.message)
Line 68: 
Line 69:     def _memToMb(self, size, unit):
Line 70:         if unit == 'bytes' or unit == 'b':
Can be little bit nicer as:

    if unit in ('bytes', 'b'):
        ...
Line 71:             return size / 1024 / 1024
Line 72:         if unit == 'KiB' or unit == 'k':
Line 73:             return size / 1024
Line 74:         elif unit == 'MiB' or unit == 'M':


Line 75:             return size
Line 76:         elif unit == 'GiB' or unit == 'G':
Line 77:             return size * 1024
Line 78:         elif unit == 'Tib' or unit == 'T':
Line 79:             return size * 1024 * 1024
else ?

If not valid input, raise. If valid, this must return a size - right?
Line 80: 
Line 81:     def _findGeneralInfo(self, root):
Line 82:         ret = {}
Line 83:         e = root.find('./uuid')


Line 89:             size = int(e.text)
Line 90:             if 'unit' in e.attrib:
Line 91:                 unit = e.attrib['unit']
Line 92:             else:
Line 93:                 unit = 'KiB'
This is cleaner and more efficient:

    unit = e.get('unit', 'KiB')
Line 94:             ret['memSize'] = self._memToMb(size, unit)
Line 95: 
Line 96:         e = root.find('./vcpu')
Line 97:         if e is not None:


Line 94:             ret['memSize'] = self._memToMb(size, unit)
Line 95: 
Line 96:         e = root.find('./vcpu')
Line 97:         if e is not None:
Line 98:             ret['smp'] = int(e.text)
Do you like to fail the entire request if the value is not valid integer?

If you do, do you really want to fail with ValueError? This error means usually that there is a bug in the program, but this is obviously bug in the client that send this value.

When you parse invalid data, you want to fail with an error from the application domain, such as InvalidVMConfiguration("bad vcpu value: 'bad value'")

When you raise such error, it is easy to handle it in the upper layer by logging an error and returning error response. There is not need for logging exceptions in this case since we know exactly what is the problem.

When you use ValueError, the upper layer must log an exception because this error may be bug in the code. This cause people to open bugs for vdsm because they found a scary exception in vdsm log.
Line 99:         return ret
Line 100: 
Line 101:     def _findDisks(self, root):
Line 102:         ret = {}


Line 99:         return ret
Line 100: 
Line 101:     def _findDisks(self, root):
Line 102:         ret = {}
Line 103:         ret['disks'] = []
Would be nicer to collect the data and create the dict at the end:

    disks = []

    for disk in disks:
        ...
        disks.append(...)

    return {'disks': disks}

Same for other methods here.
Line 104:         disks = root.findall('.//disk[@type="file"]')
Line 105:         for disk in disks:
Line 106:             d = {}
Line 107:             target = disk.find('./target')


Line 104:         disks = root.findall('.//disk[@type="file"]')
Line 105:         for disk in disks:
Line 106:             d = {}
Line 107:             target = disk.find('./target')
Line 108:             if target is not None and 'dev' in target.attrib:
This can be replaced with:

    target = disk.find('./target[@dev]')

And for getting values, it is more efficient to use get(). Using attrib the library must create a new Python dictionary, but we want only one of the values - so:

    d['dev'] = target.get('dev')

Same for the code bellow.
Line 109:                 d['dev'] = target.attrib['dev']
Line 110:             source = disk.find('./source')
Line 111:             if source is not None and 'file' in source.attrib:
Line 112:                 d['alias'] = source.attrib['file']


Line 133:             ret['networks'].append(i)
Line 134:         return ret
Line 135: 
Line 136: 
Line 137: class V2VException(Exception):
Can be removed since you do not have any module specific error.
Line 138:     code = 0
Line 139:     message = "V2V Exception"
Line 140: 
Line 141:     def __init__(self, code=0, message='V2V Exception'):


-- 
To view, visit http://gerrit.ovirt.org/33309
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: comment
Gerrit-Change-Id: I7dcfb860626a844d1d08590274b508519a33f4a3
Gerrit-PatchSet: 14
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Shahar Havivi <shavivi at redhat.com>
Gerrit-Reviewer: Dan Kenigsberg <danken at redhat.com>
Gerrit-Reviewer: Francesco Romani <fromani at redhat.com>
Gerrit-Reviewer: Nir Soffer <nsoffer at redhat.com>
Gerrit-Reviewer: Shahar Havivi <shavivi at redhat.com>
Gerrit-Reviewer: automation at ovirt.org
Gerrit-Reviewer: oVirt Jenkins CI Server
Gerrit-HasComments: Yes


More information about the vdsm-patches mailing list