class Nodes(object):
    """
	UMC functions for UVMM node handling.
	"""
    @sanitize(nodePattern=SearchSanitizer(default='*'))
    def node_query(self, request):
        """
		Searches nodes by the given pattern

		options: {'nodePattern': <pattern>}

		return: [{
			'id': <node URI>,
			'label': <node name>,
			'group': 'default',
			'type': 'node',
			'virtech': <virtualization technology>,
			'memUsed': <used amount of memory in B>,
			'memAvailable': <amount of physical memory in B>,
			'cpus': <number of CPUs>,
			'cpuUsage': <cpu usage in %>,
			'available': (True|False),
			'supports_suspend': (True|False),
			'supports_snapshot': (True|False)
			}, ...]
		"""
        def _finished(data):
            """
			Process asynchronous UVMM NODE_LIST answer.
			"""

            nodes = []
            for node_pd in data:
                node_uri = urlsplit(node_pd.uri)
                nodes.append({
                    'id': node_pd.uri,
                    'label': node_pd.name,
                    'group': _('Physical servers'),
                    'type': 'node',
                    'virtech': node_uri.scheme,
                    'memUsed': node_pd.curMem,
                    'memAvailable': node_pd.phyMem,
                    'cpuUsage': (node_pd.cpu_usage or 0) / 10.0,
                    'available': node_pd.last_try == node_pd.last_update,
                    'cpus': node_pd.cpus,
                    'supports_suspend': node_pd.supports_suspend,
                    'supports_snapshot': node_pd.supports_snapshot,
                })
            return nodes

        self.uvmm.send('NODE_LIST',
                       self.process_uvmm_response(request, _finished),
                       group='default',
                       pattern=request.options['nodePattern'])
Esempio n. 2
0
class Cloud(object):
    """
	Handle cloud connections and instances.
	"""
    @sanitize(nodePattern=SearchSanitizer(default='*'))
    def cloud_query(self, request):
        """
		Searches clouds by the given pattern

		options: {'nodePattern': <cloud pattern>}

		return: [{
			'id': <cloud name>,
			'label': <cloud name>,
			'group': 'cloudconnection',
			'type': 'cloud',
			'available': (True|False),
			}, ...]
		"""
        self.required_options(request, 'nodePattern')

        def _finished(data):
            """
			Process asynchronous UVMM L_CLOUD_LIST answer.
			"""
            return [{
                'id': d.name,
                'label': d.name,
                'group': _('Cloud connection'),
                'type': 'cloud',
                'cloudtype': d.cloudtype,
                'available': d.available,
                'last_error_message': d.last_error_message,
                'dn': d.dn,
                'search_pattern': d.search_pattern,
                'ucs_images': d.ucs_images,
            } for d in data]

        self.uvmm.send('L_CLOUD_LIST',
                       self.process_uvmm_response(request, _finished),
                       pattern=request.options['nodePattern'])

    def cloud_add(self, request):
        """
		Add a new cloud connection into ldap.
		options: {
			['cloudtype': <uvmm/cloudtype>,]
			['name': <new cloud name>,]
			['parameter': <key/value parameter>,]
			['testconnection': true (default) / false,]
			}

		return: []
		"""
        def _finished(data):
            # add cloud to ldap
            ldap_cloud_connection_add(cloudtype, name, parameter, ucs_images,
                                      search_pattern, preselected_images)
            return data

        self.required_options(request, 'cloudtype', 'name', 'parameter',
                              'testconnection')
        cloudtype = request.options.get('cloudtype')
        name = request.options.get('name')
        testconnection = request.options.get('testconnection')
        parameter = request.options.get('parameter', {})
        search_pattern = parameter.pop('search_pattern', '')
        preselected_images = parameter.pop('preselected_images', [])
        ucs_images = parameter.pop('ucs_images', True)

        # add cloud to uvmm
        args = parameter.copy()
        args['name'] = name
        args['type'] = cloudtype
        args['search_pattern'] = search_pattern
        args['preselected_images'] = preselected_images
        args['ucs_images'] = ucs_images

        self.uvmm.send('L_CLOUD_ADD',
                       self.process_uvmm_response(request, _finished),
                       args=args,
                       testconnection=testconnection)

    def cloud_list_keypair(self, request):
        """
		Returns a list of keypair for the given cloud conn_name.
		"""
        self.required_options(request, 'conn_name')
        conn_name = request.options.get('conn_name')

        def _finished(data):
            """
			Process asynchronous UVMM L_CLOUD_KEYPAIR_LIST answer.
			"""
            return [{
                'id': item.name,
                'label': item.name
            } for conn_name, images in data.items() for item in images]

        self.uvmm.send('L_CLOUD_KEYPAIR_LIST',
                       self.process_uvmm_response(request, _finished),
                       conn_name=conn_name)

    def cloud_list_size(self, request):
        """
		Returns a list of hardware sizes for the given cloud conn_name.
		"""
        self.required_options(request, 'conn_name')
        conn_name = request.options.get('conn_name')

        def _finished(data):
            """
			Process asynchronous UVMM L_CLOUD_SIZE_LIST answer.
			"""
            size_list = []
            for conn_name, images in data.items():
                for item in images:
                    size_list.append({
                        'id': item.id,
                        'label': item.u_displayname,
                        'disk': item.disk,
                        'ram': item.ram,
                        'vcpus': item.vcpus,
                    })

            return size_list

        self.uvmm.send('L_CLOUD_SIZE_LIST',
                       self.process_uvmm_response(request, _finished),
                       conn_name=conn_name)

    @sanitize(pattern=SearchSanitizer(default='*'))
    def cloud_list_image(self, request):
        """
		Returns a list of images by a pattern for the given cloud conn_name.
		"""
        self.required_options(request, 'conn_name')
        conn_name = request.options.get('conn_name')

        def _finished(data):
            """
			Process asynchronous UVMM L_CLOUD_IMAGE_LIST answer.
			"""
            return [{
                'id': item.id,
                'label': item.name
            } for conn_name, images in data.items() for item in images]

        self.uvmm.send('L_CLOUD_IMAGE_LIST',
                       self.process_uvmm_response(request, _finished),
                       conn_name=conn_name)

    def cloud_list_secgroup(self, request):
        """
		Returns a list of security groups for the given cloud conn_name.
		"""
        self.required_options(request, 'conn_name')
        conn_name = request.options.get('conn_name')
        network_id = request.options.get('network_id')

        def _finished(data):
            """
			Process asynchronous UVMM L_CLOUD_SECGROUP_LIST answer.
			"""
            return [{
                'id': item.id,
                'label': item.name
            } for conn_name, images in data.items() for item in images
                    if network_id in ('default', item.network_id)]

        self.uvmm.send('L_CLOUD_SECGROUP_LIST',
                       self.process_uvmm_response(request, _finished),
                       conn_name=conn_name)

    def cloud_list_network(self, request):
        """
		Returns a list of networks for the given cloud conn_name.
		"""
        self.required_options(request, 'conn_name')
        conn_name = request.options.get('conn_name')

        def _finished(data):
            """
			Process asynchronous UVMM L_CLOUD_NETWORK_LIST answer.
			"""
            return [{
                'id': item.id,
                'label': '%s %s' % (item.name, item.cidr or "")
            } for conn_name, images in data.items() for item in images]

        self.uvmm.send('L_CLOUD_NETWORK_LIST',
                       self.process_uvmm_response(request, _finished),
                       conn_name=conn_name)

    def cloud_list_subnet(self, request):
        """
		Returns a list of subnet for the given cloud conn_name.
		"""
        self.required_options(request, 'conn_name')
        conn_name = request.options.get('conn_name')
        network_id = request.options.get('network_id')

        def _finished(data):
            """
			Process asynchronous UVMM L_CLOUD_SUBNET_LIST answer.
			"""
            return [{
                'id': item.id,
                'label': '%s %s' % (item.name, item.cidr or "")
            } for conn_name, images in data.items() for item in images
                    if network_id == item.network_id]

        self.uvmm.send('L_CLOUD_SUBNET_LIST',
                       self.process_uvmm_response(request, _finished),
                       conn_name=conn_name)

    @sanitize(domainPattern=SearchSanitizer(default='*'))
    def instance_query(self, request):
        """
		Returns a list of instances matching domainPattern on the clouds matching nodePattern.

		options: {
			['nodePattern': <cloud pattern>,]
			['domainPattern': <instance pattern>,]
			}

		return: [{
			'node_available': True,
			'extra': {
				'key_name': None,
				'disk_config': 'MANUAL',
				'flavorId': '1',
				'availability_zone': 'nova',
				'password': None,
				'metadata': {}
			},
			'label': 'automagic-997898',
			'type': 'instance',
			'id': 'myCloud2#e2c8e274-2e17-499c-a3f9-620fb249578c',
			'nodeName': 'myCloud2'
		}, ... ]
		"""
        def _finished(data):
            """
			Process asynchronous UVMM L_CLOUD_INSTANCE_LIST answer.
			"""
            instances = []
            for conn_name, insts in data.items():
                for inst in insts:
                    instance_uri = '%s#%s' % (conn_name, inst.id)
                    instances.append({
                        'id':
                        instance_uri,
                        'label':
                        inst.name,
                        'nodeName':
                        conn_name,
                        'state':
                        inst.state,
                        'type':
                        'instance',
                        'suspended':
                        None,  # FIXME
                        'description':
                        '%s [%s]' % (inst.u_size_name, inst.state),
                        'node_available':
                        inst.available,
                        'extra':
                        inst.extra,
                        'public_ips':
                        inst.public_ips,
                        'private_ips':
                        inst.private_ips,
                        'u_size_name':
                        inst.u_size_name,
                        'u_connection_type':
                        inst.u_connection_type,
                        'keypair':
                        inst.key_name,
                        'image':
                        inst.u_image_name,
                        'securitygroup':
                        inst.secgroups,
                    })
            return instances

        self.uvmm.send('L_CLOUD_INSTANCE_LIST',
                       self.process_uvmm_response(request, _finished),
                       conn_name=request.options.get('nodePattern', ''),
                       pattern=request.options['domainPattern'])

    @sanitize(state=ChoicesSanitizer(choices=('RUN', 'RESTART', 'SOFTRESTART',
                                              'SHUTOFF', 'SHUTDOWN', 'SUSPEND',
                                              'PAUSE', 'RESUME', 'UNPAUSE')))
    def instance_state(self, request):
        """
		Set the state a instance instance_id on cloud conn_name.

		options: {
			'uri': <conn_name#instance_id>,
			'state': (RUN|RESTART|SOFTRESTART|SHUTOFF|SHUTDOWN|SUSPEND|RESUME|UNPAUSE),
			}

		return:
		"""
        self.required_options(request, 'uri', 'state')

        conn_name, instance_id = urldefrag(request.options['uri'])
        state = request.options['state']

        self.uvmm.send(
            'L_CLOUD_INSTANCE_STATE',
            self.process_uvmm_response(request),
            conn_name=conn_name,
            instance_id=instance_id,
            state=state,
        )

    def instance_remove(self, request):
        """
		Removes a instance.

		options: {
			'domainURI': <domain uri>
			}

		return:
		"""
        self.required_options(request, 'domainURI')
        conn_name, instance_id = urldefrag(request.options['domainURI'])

        self.uvmm.send('L_CLOUD_INSTANCE_TERMINATE',
                       self.process_uvmm_response(request),
                       conn_name=conn_name,
                       instance_id=instance_id)

    def instance_add(self, request):
        """
		Create a new instance on cloud conn_name.

		options: {
			'conn_name': <cloud connection name>,
			'parameter': {...},
			}

		return:
		"""
        self.required_options(request, 'conn_name', 'name', 'parameter')
        conn_name = request.options.get('conn_name')
        name = request.options.get('name')
        parameter = request.options.get('parameter')

        args = parameter
        args['name'] = name
        args['security_group_ids'] = [parameter['security_group_ids']]

        self.uvmm.send('L_CLOUD_INSTANCE_CREATE',
                       self.process_uvmm_response(request),
                       conn_name=conn_name,
                       args=args)

    def cloudtype_get(self, request):
        """
		Returns a list of all cloudtypes from ldap.
		"""
        cloudtypes = []
        for item in ldap_cloud_types():
            cloudtypes.append({'id': item['name'], 'label': item['name']})

        self.finished(request.id, cloudtypes)
Esempio n. 3
0
class Domains(object):
    """
	UMC functions for UVMM domain handling.
	"""

    STATES = ('NOSTATE', 'RUNNING', 'IDLE', 'PAUSED', 'SHUTDOWN', 'SHUTOFF',
              'CRASHED')
    TARGET_STATES = ('RUN', 'PAUSE', 'SHUTDOWN', 'SHUTOFF', 'RESTART',
                     'SUSPEND')

    RE_VNC = re.compile(r'^(IPv[46])(?: (.+))?$|^(?:NAME(?: (.+=.*))?)$')
    SOCKET_FAMILIES = {
        'IPv4': socket.AF_INET,
        'IPv6': socket.AF_INET6,
    }
    SOCKET_FORMATS = {
        socket.AF_INET: '%s',
        socket.AF_INET6: '[%s]',
    }

    @sanitize(domainPattern=SearchSanitizer(default='*'))
    def domain_query(self, request):
        """
		Returns a list of domains matching domainPattern on the nodes matching nodePattern.

		options: {
			['nodePattern': <node name pattern>,]
			['domainPattern': <domain pattern>,]
			}

		return: [{
			'cpuUsage': <float>,
			'description': <string>,
			'id': <domain uri>,
			'label': <name>,
			'mem': <ram>,
			'nodeName': <node>,
			'node_available': <boolean>,
			'state': <state>,
			'reason': <reason>,
			'suspended': <boolean>,
			'type': 'domain',
			'vnc': <boolean>,
			'vnc_port': <int>,
			'migration': <dict>,
			}, ...]
		"""
        def _finished(data):
            """
			Process asynchronous UVMM DOMAIN_LIST answer.
			"""
            domain_list = []
            for node_uri, domains in data.items():
                uri = urlsplit(node_uri)
                for domain in domains:
                    domain_uri = '%s#%s' % (node_uri, domain['uuid'])
                    domain_list.append({
                        'id':
                        domain_uri,
                        'label':
                        domain['name'],
                        'nodeName':
                        uri.netloc,
                        'state':
                        domain['state'],
                        'reason':
                        domain['reason'],
                        'type':
                        'domain',
                        'mem':
                        domain['mem'],
                        'cpuUsage':
                        domain['cpu_usage'],
                        'vnc':
                        domain['vnc'],
                        'vnc_port':
                        domain['vnc_port'],
                        'suspended':
                        bool(domain['suspended']),
                        'description':
                        domain['description'],
                        'node_available':
                        domain['node_available'],
                        'error':
                        domain['error'],
                        'migration':
                        domain['migration'],
                    })
            return domain_list

        self.uvmm.send('DOMAIN_LIST',
                       self.process_uvmm_response(request, _finished),
                       uri=request.options.get('nodePattern', ''),
                       pattern=request.options['domainPattern'])

    def domain_get(self, request):
        """
		Returns details about a domain domainUUID.

		options: {'domainURI': <domain uri>}

		return: {...}
		"""
        def _finished(data):
            """
			Process asynchronous UVMM DOMAIN_INFO answer.
			Convert UVMM protocol to JSON.
			"""
            node_uri = urlsplit(request.options['domainURI'])
            uri, _uuid = urldefrag(request.options['domainURI'])
            json = object2dict(data)

            # re-arrange a few attributes for the frontend
            # annotations
            for key in json['annotations']:
                if key == 'uuid':
                    continue
                json[key] = json['annotations'][key]

            # STOP here if domain is not available
            if not json['available']:
                MODULE.info('Domain is not available: %s' % (json, ))
                self.finished(request.id, json)
                return

            # interfaces (fake the special type network:<source>)
            for iface in json['interfaces']:
                if iface['type'] == Interface.TYPE_NETWORK:
                    iface['type'] = 'network:' + iface['source']

            # disks
            for disk in json['disks']:
                disk['volumeFilename'] = os.path.basename(
                    disk['source']) if disk['pool'] else disk['source']
                disk['paravirtual'] = disk['target_bus'] in ('virtio', )
                disk['volumeType'] = disk['type']

            # graphics
            if json['graphics']:
                try:
                    gfx = json['graphics'][0]
                    json['vnc'] = True
                    json['vnc_host'] = None
                    json['vnc_port'] = None
                    json['kblayout'] = gfx['keymap']
                    json['vnc_remote'] = gfx['listen'] == '0.0.0.0'
                    json['vnc_password'] = gfx['passwd']
                    # vnc_password will not be send to frontend
                    port = int(json['graphics'][0]['port'])
                    if port == -1:
                        raise ValueError(json['graphics'][0]['port'])
                    host = node_uri.netloc
                    vnc_link_format = ucr.get('uvmm/umc/vnc/host',
                                              'IPv4') or ''
                    match = Domains.RE_VNC.match(vnc_link_format)
                    if match:
                        family, pattern, substs = match.groups()
                        if family:  # IPvX
                            family = Domains.SOCKET_FAMILIES[family]
                            regex = re.compile(pattern or '.*')
                            addrs = socket.getaddrinfo(host, port, family,
                                                       socket.SOCK_STREAM,
                                                       socket.SOL_TCP)
                            for (family, _socktype, _proto, _canonname,
                                 sockaddr) in addrs:
                                host, port = sockaddr[:2]
                                if regex.search(host):
                                    break
                            else:
                                raise LookupError(pattern)
                            host = Domains.SOCKET_FORMATS[family] % (host, )
                        elif substs:  # NAME
                            for subst in substs.split():
                                old, new = subst.split('=', 1)
                                host = host.replace(old, new)
                    elif vnc_link_format:  # overwrite all hosts with fixed host
                        host = vnc_link_format
                    json['vnc_host'] = host
                    json['vnc_port'] = port
                except re.error as ex:  # port is not valid
                    MODULE.warn('Invalid VNC regex: %s' % (ex, ))
                except socket.gaierror as ex:
                    MODULE.warn('Invalid VNC host: %s' % (ex, ))
                except (ValueError, LookupError) as ex:  # port is not valid
                    MODULE.warn('Failed VNC lookup: %s' % (ex, ))

            # profile (MUST be after mapping annotations)
            profile_dn = json.get('profile')
            profile = None
            if profile_dn:
                for dn, pro in self.profiles:
                    if dn == profile_dn:
                        profile = pro
                        break
                if profile:
                    json['profileData'] = object2dict(profile)

            MODULE.info('Got domain description: %s' % (json, ))
            return json

        self.required_options(request, 'domainURI')
        node_uri, domain_uuid = urldefrag(request.options['domainURI'])
        self.uvmm.send('DOMAIN_INFO',
                       self.process_uvmm_response(request, _finished),
                       uri=node_uri,
                       domain=domain_uuid)

    def _create_disk(self, node_uri, disk, domain_info, profile=None):
        """
		Convert single disk from JSON to Python UVMM Disk object.
		"""
        uri = urlsplit(node_uri)

        driver_pv = disk.get('paravirtual',
                             False)  # by default no paravirtual devices

        drive = Disk()
        drive.device = disk['device']
        drive.driver_type = disk['driver_type']
        drive.driver_cache = disk.get('driver_cache', 'default')
        drive.driver = disk.get('driver', None)
        drive.target_bus = disk.get('target_bus', 'ide')
        drive.target_dev = disk.get('target_dev', None)

        pool_name = disk.get('pool')
        if pool_name:
            pool = self.get_pool(node_uri, pool_name)
        else:
            pool = {}

        if disk.get('source', None) is None:
            # new drive
            drive.size = disk['size']
            if not pool:
                raise ValueError('Pool "%s" not found' % (pool_name, ))
            drive.source = os.path.join(pool['path'], disk['volumeFilename'])

            if profile:
                if drive.device == Disk.DEVICE_DISK:
                    driver_pv = getattr(profile, 'pvdisk', False)
                elif drive.device == Disk.DEVICE_CDROM:
                    driver_pv = getattr(profile, 'pvcdrom', False)
        else:
            # old drive
            drive.source = disk['source']

        MODULE.info('Creating a %s drive' %
                    ('paravirtual' if driver_pv else 'emulated', ))

        try:
            pool_type = pool['type']
            drive.type = POOLS_TYPE[pool_type]
        except LookupError:
            if drive.source.startswith('/dev/'):
                drive.type = Disk.TYPE_BLOCK
            elif not drive.source:
                # empty CDROM or floppy device
                drive.type = Disk.TYPE_BLOCK
            else:
                drive.type = Disk.TYPE_FILE

        if drive.device == Disk.DEVICE_DISK:
            drive.readonly = disk.get('readonly', False)
        elif drive.device == Disk.DEVICE_CDROM:
            drive.driver_type = 'raw'  # ISOs need driver/@type='raw'
            drive.readonly = disk.get('readonly', True)
        elif drive.device == Disk.DEVICE_FLOPPY:
            drive.readonly = disk.get('readonly', True)
        else:
            raise ValueError('Invalid drive-type "%s"' % drive.device)

        if uri.scheme.startswith('qemu'):
            drive.driver = 'qemu'
            if drive.device == Disk.DEVICE_FLOPPY:
                drive.target_bus = 'fdc'
            elif driver_pv:
                drive.target_bus = 'virtio'
            elif disk.get('paravirtual', None) is False:
                drive.target_bus = 'ide'
            else:
                pass  # keep
        else:
            raise ValueError('Unknown virt-tech "%s"' % (node_uri, ))

        return drive

    def domain_add(self, request):
        """
		Creates a new domain on nodeURI.

		options: {
			'nodeURI': <node uri>,
			'domain': {...},
			}

		return:
		"""
        self.required_options(request, 'nodeURI', 'domain')

        domain = request.options.get('domain')

        domain_info = Data_Domain()
        # when we edit a domain there must be a UUID
        if 'domainURI' in domain:
            _node_uri, domain_uuid = urldefrag(domain['domainURI'])
            domain_info.uuid = domain_uuid

        # annotations & profile
        profile = None
        if not domain_info.uuid:
            profile_dn = domain.get('profile')
            for dn, pro in self.profiles:
                if dn == profile_dn:
                    profile = pro
                    break
            else:
                raise UMC_Error(_('Unknown profile given'))
            domain_info.annotations['profile'] = profile_dn
            MODULE.info('Creating new domain using profile %s' %
                        (object2dict(profile), ))
            domain_info.annotations['os'] = getattr(profile, 'os')
        else:
            domain_info.annotations['os'] = domain.get('os', '')
        domain_info.annotations['contact'] = domain.get('contact', '')
        domain_info.annotations['description'] = domain.get('description', '')

        domain_info.name = domain['name']
        if 'arch' in domain:
            domain_info.arch = domain['arch']
        elif profile:
            domain_info.arch = profile.arch
        else:
            raise UMC_Error(_('Could not determine architecture for domain'),
                            status=500)

        if domain_info.arch == 'automatic':
            success, node_list = self.uvmm.send(
                'NODE_LIST',
                None,
                group='default',
                pattern=request.options['nodeURI'])
            if not success:
                raise UMC_Error(
                    _('Failed to retrieve details for the server %(nodeURI)s')
                    % request.options,
                    status=500)
            if not node_list:
                raise UMC_Error(_('Unknown physical server %(nodeURI)s') %
                                request.options,
                                status=500)
            archs = set([t.arch for t in node_list[0].capabilities])
            if 'x86_64' in archs:
                domain_info.arch = 'x86_64'
            else:
                domain_info.arch = 'i686'

        domain_info.domain_type = 'kvm'
        domain_info.os_type = 'hvm'
        domain_info.maxMem = domain['maxMem']

        # CPUs
        try:
            domain_info.vcpus = int(domain['vcpus'])
        except ValueError:
            raise UMC_Error(_('vcpus must be a number'))
        domain_info.hyperv = domain.get('hyperv', True)

        # boot devices
        if 'boot' in domain:
            domain_info.boot = domain['boot']
        elif profile:
            domain_info.boot = getattr(profile, 'bootdev', None)
        else:
            raise UMC_Error(
                _('Could not determine the list of boot devices for domain'),
                status=500)

        # VNC
        if domain['vnc']:
            gfx = Graphic()
            if domain.get('vnc_remote', False):
                gfx.listen = '0.0.0.0'
            else:
                gfx.listen = None
            if 'kblayout' in domain:
                gfx.keymap = domain['kblayout']
            elif profile:
                gfx.keymap = profile.kblayout
            else:
                raise UMC_Error(_(
                    'Could not determine the keyboard layout for the VNC access'
                ),
                                status=500)
            if domain.get('vnc_password', None):
                gfx.passwd = domain['vnc_password']

            domain_info.graphics = [gfx]

        # RTC offset
        if 'rtc_offset' in domain:
            domain_info.rtc_offset = domain['rtc_offset']
        elif profile and getattr(profile, 'rtcoffset'):
            domain_info.rtc_offset = profile.rtcoffset
        else:
            domain_info.rtc_offset = 'utc'

        # drives
        domain_info.disks = [
            self._create_disk(request.options['nodeURI'], disk, domain_info,
                              profile) for disk in domain['disks']
        ]

        # network interface
        domain_info.interfaces = []
        for interface in domain['interfaces']:
            iface = Interface()
            if interface.get('type', '').startswith('network:'):
                iface.type = 'network'
                iface.source = interface['type'].split(':', 1)[1]
            else:
                iface.type = interface.get('type', 'bridge')
                iface.source = interface['source']
            iface.model = interface['model']
            iface.mac_address = interface.get('mac_address', None)
            domain_info.interfaces.append(iface)

        def _finished(data):
            """
			Process asynchronous UVMM DOMAIN_DEFINE answer.
			"""
            return object2dict(data)

        self.uvmm.send('DOMAIN_DEFINE',
                       self.process_uvmm_response(request, _finished),
                       uri=request.options['nodeURI'],
                       domain=domain_info)

    domain_put = domain_add

    def domain_state(self, request):
        """
		Set the state a domain domainUUID on node nodeURI.

		options: {
			'domainURI': <domain uri>,
			'domainState': (RUN|SHUTDOWN|SHUTOFF|PAUSE|RESTART|SUSPEND),
			}

		return:
		"""
        self.required_options(request, 'domainURI', 'domainState')
        node_uri, domain_uuid = urldefrag(request.options['domainURI'])
        MODULE.info('nodeURI: %s, domainUUID: %s' % (node_uri, domain_uuid))
        state = request.options['domainState']
        if state not in self.TARGET_STATES:
            raise UMC_Error(_('Invalid domain state: %s') % state)
        self.uvmm.send(
            'DOMAIN_STATE',
            self.process_uvmm_response(request),
            uri=node_uri,
            domain=domain_uuid,
            state=state,
        )

    def domain_migrate(self, request):
        """
		Migrates a domain from sourceURI to targetURI.

		options: {
			'domainURI': <domain uri>,
			'targetNodeURI': <target node uri>,
			}

		return:
		"""
        self.required_options(request, 'domainURI')
        node_uri, domain_uuid = urldefrag(request.options['domainURI'])
        mode = request.options.get('mode', 0)
        if mode < 101 and mode > -1:
            self.required_options(request, 'targetNodeURI')
        target_uri = request.options.get('targetNodeURI', '')
        self.uvmm.send('DOMAIN_MIGRATE',
                       self.process_uvmm_response(request),
                       uri=node_uri,
                       domain=domain_uuid,
                       target_uri=target_uri,
                       mode=mode)

    def domain_clone(self, request):
        """
		Clones an existing domain.

		options: {
			'domainURI': <domain uri>,
			'cloneName': <name of clone>,
			'macAddress' : (clone|auto),
			}

		return:
		"""
        self.required_options(request, 'domainURI', 'cloneName')
        node_uri, domain_uuid = urldefrag(request.options['domainURI'])
        self.uvmm.send(
            'DOMAIN_CLONE',
            self.process_uvmm_response(request),
            uri=node_uri,
            domain=domain_uuid,
            name=request.options['cloneName'],
            subst={'mac': request.options.get('macAddress', 'clone')})

    def domain_remove(self, request):
        """
		Removes a domain. Optional a list of volumes can be specified that should be removed.

		options: {
			'domainURI': <domain uri>,
			'volumes': [<filename>...]
			}

		return:
		"""
        self.required_options(request, 'domainURI', 'volumes')
        node_uri, domain_uuid = urldefrag(request.options['domainURI'])
        volume_list = request.options['volumes']
        self.uvmm.send('DOMAIN_UNDEFINE',
                       self.process_uvmm_response(request),
                       uri=node_uri,
                       domain=domain_uuid,
                       volumes=volume_list)