def _storage_validate(self): pool_uri = self.info['storagepool'] pool_name = pool_name_from_uri(pool_uri) try: conn = self.conn.get() pool = conn.storagePoolLookupByName(pool_name.encode("utf-8")) except libvirt.libvirtError: raise InvalidParameter("KCHTMPL0004E", { 'pool': pool_name, 'template': self.name }) if not pool.isActive(): raise InvalidParameter("KCHTMPL0005E", { 'pool': pool_name, 'template': self.name }) return pool
def create(self, params): ident = params.get('name').strip() # Generate a name with time and millisec precision, if necessary if ident is None or ident == "": ident = 'report-' + str(int(time.time() * 1000)) else: if ident in self.get_list(): raise InvalidParameter("KCHDR0008E", {"name": ident}) taskid = self._gen_debugreport_file(ident) return self.task.lookup(taskid)
def clone(self, name): """Clone a virtual machine based on an existing one. The new virtual machine will have the exact same configuration as the original VM, except for the name, UUID, MAC addresses and disks. The name will have the form "<name>-clone-<number>", with <number> starting at 1; the UUID will be generated randomly; the MAC addresses will be generated randomly with no conflicts within the original and the new VM; and the disks will be new volumes [mostly] on the same storage pool, with the same content as the original disks. The storage pool 'default' will always be used when cloning SCSI and iSCSI disks and when the original storage pool cannot hold the new volume. An exception will be raised if the virtual machine <name> is not shutoff, if there is no available space to copy a new volume to the storage pool 'default' (when there was also no space to copy it to the original storage pool) and if one of the virtual machine's disks belong to a storage pool not supported by Kimchi. Parameters: name -- The name of the existing virtual machine to be cloned. Return: A Task running the clone operation. """ name = name.decode('utf-8') # VM must be shutoff in order to clone it info = self.lookup(name) if info['state'] != u'shutoff': raise InvalidParameter('KCHVM0033E', {'name': name}) # the new VM's name will be used as the Task's 'target_uri' so it needs # to be defined now. vms_being_created = [] # lookup names of VMs being created right now with self.objstore as session: task_names = session.get_list('task') for tn in task_names: t = session.get('task', tn) if t['target_uri'].startswith('/vms/'): uri_name = t['target_uri'][5:] # 5 = len('/vms/') vms_being_created.append(uri_name) current_vm_names = self.vms.get_list() + vms_being_created new_name = get_next_clone_name(current_vm_names, name) # create a task with the actual clone function taskid = add_task(u'/vms/%s' % new_name, self._clone_task, self.objstore, {'name': name, 'new_name': new_name}) return self.task.lookup(taskid)
def validate_params(params, instance, action): root = cherrypy.request.app.root if hasattr(root, 'api_schema'): api_schema = root.api_schema else: return operation = model_fn(instance, action) validator = Draft3Validator(api_schema, format_checker=FormatChecker()) request = {operation: params} try: validator.validate(request) except ValidationError, e: if e.schema.get('error'): raise InvalidParameter(e.schema['error'], {'value': str(e.instance)}) else: raise InvalidParameter("KCHAPI0008E", {"err": str(e.message)})
def delete(self, pool, name): pool_info = StoragePoolModel(conn=self.conn, objstore=self.objstore).lookup(pool) if pool_info['type'] in READONLY_POOL_TYPE: raise InvalidParameter("KCHVOL0012E", {'type': pool_info['type']}) volume = StorageVolumeModel.get_storagevolume(pool, name, self.conn) try: volume.delete(0) except libvirt.libvirtError as e: raise OperationFailed("KCHVOL0010E", {'name': name, 'err': e.get_error_message()})
def addRepository(self, params): """ Add and enable a new repository """ config = params.get('config', {}) extra_keys = list( set(config.keys()).difference(set(self._pkg_mnger.CONFIG_ENTRY))) if len(extra_keys) > 0: raise InvalidParameter("KCHREPOS0028E", {'items': ",".join(extra_keys)}) return self._pkg_mnger.addRepo(params)
def update(self, name, params): old_t = self.lookup(name) new_t = copy.copy(old_t) new_t.update(params) ident = name conn = self.conn.get() pool_uri = new_t.get(u'storagepool', '') if pool_uri: try: pool_name = pool_name_from_uri(pool_uri) pool = conn.storagePoolLookupByName(pool_name.encode("utf-8")) except Exception: raise InvalidParameter("KCHTMPL0004E", { 'pool': pool_name, 'template': name }) tmp_volumes = [ disk['volume'] for disk in new_t.get('disks', []) if 'volume' in disk ] self.templates.template_volume_validate(tmp_volumes, pool) for net_name in params.get(u'networks', []): try: conn.networkLookupByName(net_name) except Exception: raise InvalidParameter("KCHTMPL0003E", { 'network': net_name, 'template': name }) self.delete(name) try: ident = self.templates.create(new_t) except: ident = self.templates.create(old_t) raise return ident
def _set_network_bridge(self, params): try: iface = params['interface'] if iface in self.get_all_networks_interfaces(): msg_args = {'iface': iface, 'network': params['name']} raise InvalidParameter("KCHNET0006E", msg_args) except KeyError: raise MissingParameter("KCHNET0004E", {'name': params['name']}) self._ensure_iface_up(iface) if netinfo.is_bridge(iface): if 'vlan_id' in params: raise InvalidParameter('KCHNET0019E', {'name': iface}) params['bridge'] = iface elif netinfo.is_bare_nic(iface) or netinfo.is_bonding(iface): if params.get('vlan_id') is None: params['forward']['dev'] = iface else: params['bridge'] = \ self._create_vlan_tagged_bridge(str(iface), str(params['vlan_id'])) else: raise InvalidParameter("KCHNET0007E")
def vmifaces_create(self, vm, params): if (params["type"] == "network" and params["network"] not in self.networks_get_list()): msg_args = {'network': params["network"], 'name': vm} raise InvalidParameter("KCHVMIF0002E", msg_args) dom = self._get_vm(vm) iface = MockVMIface(params["network"]) ("model" in params.keys() and iface.info.update({"model": params["model"]})) mac = iface.info['mac'] dom.ifaces[mac] = iface return mac
def template_volume_validate(self, tmp_volumes, pool): kwargs = {'conn': self.conn, 'objstore': self.objstore} pool_type = xpath_get_text(pool.XMLDesc(0), "/pool/@type")[0] pool_name = unicode(pool.name(), 'utf-8') # as we discussion, we do not mix disks from 2 different types of # storage pools, for instance: we do not create a template with 2 # disks, where one comes from a SCSI pool and other is a .img in # a DIR pool. if pool_type in ['iscsi', 'scsi']: if not tmp_volumes: raise InvalidParameter("KCHTMPL0018E") storagevolumes = __import__("kimchi.model.storagevolumes", fromlist=['']) pool_volumes = storagevolumes.StorageVolumesModel( **kwargs).get_list(pool_name) vols = set(tmp_volumes) - set(pool_volumes) if vols: raise InvalidParameter("KCHTMPL0019E", { 'pool': pool_name, 'volume': vols })
def create(self, params): name = params.get('name', '').strip() if not name: iso = params['cdrom'] iso_name = os.path.splitext(iso[iso.rfind('/') + 1:])[0] name = iso_name + str(int(time.time() * 1000)) params['name'] = name conn = self.conn.get() pool_uri = params.get(u'storagepool', '') if pool_uri: pool_name = pool_name_from_uri(pool_uri) try: conn.storagePoolLookupByName(pool_name.encode("utf-8")) except Exception: raise InvalidParameter("KCHTMPL0004E", { 'pool': pool_name, 'template': name }) for net_name in params.get(u'networks', []): try: conn.networkLookupByName(net_name) except Exception: raise InvalidParameter("KCHTMPL0003E", { 'network': net_name, 'template': name }) with self.objstore as session: if name in session.get_list('template'): raise InvalidOperation("KCHTMPL0001E", {'name': name}) t = LibvirtVMTemplate(params, scan=True) session.store('template', name, t.info) return name
def update(self, name, params): path = config.get_debugreports_path() file_pattern = os.path.join(path, name + '.*') try: file_source = glob.glob(file_pattern)[0] except IndexError: raise NotFoundError("KCHDR0001E", {'name': name}) file_target = file_source.replace(name, params['name']) if os.path.isfile(file_target): raise InvalidParameter('KCHDR0008E', {'name': params['name']}) shutil.move(file_source, file_target) kimchi_log.info('%s renamed to %s' % (file_source, file_target)) return params['name']
def _vm_update_access_metadata(self, dom, params): users = groups = None if "users" in params: users = params["users"] for user in users: if not self.users.validate(user): raise InvalidParameter("KCHVM0027E", {'users': user}) if "groups" in params: groups = params["groups"] for group in groups: if not self.groups.validate(group): raise InvalidParameter("KCHVM0028E", {'groups': group}) if users is None and groups is None: return old_users, old_groups = self._get_access_info(dom) users = old_users if users is None else users groups = old_groups if groups is None else groups node = self._build_access_elem(users, groups) set_metadata_node(dom, node, self.caps.metadata_support)
def updateRepository(self, repo_id, new_repo={}): """ Update the information of a given repository. The input is the repo_id of the repository to be updated and a dict with the information to be updated. """ if (len(new_repo) == 0): raise InvalidParameter("KCHREPOS0013E") repo = self._repo_storage[repo_id] repo.update(new_repo) del self._repo_storage[repo_id] self._repo_storage[repo_id] = repo self._pkg_mnger.updateRepo(repo_id, self._repo_storage[repo_id])
def _static_vm_update(self, dom, params): state = dom.info['state'] if 'name' in params: if state == 'running' or params['name'] in self.vms_get_list(): msg_args = {'name': dom.name, 'new_name': params['name']} raise InvalidParameter("KCHVM0003E", msg_args) else: del self._mock_vms[dom.name] dom.name = params['name'] self._mock_vms[dom.name] = dom for key, val in params.items(): if key in VM_STATIC_UPDATE_PARAMS and key in dom.info: dom.info[key] = val
def _check_cdrom_path(path): if check_url_path(path): src_type = 'network' # Check if path is a valid local path elif os.path.exists(path): if os.path.isfile(path): src_type = 'file' else: # Check if path is a valid cdrom drive with open('/proc/sys/dev/cdrom/info') as cdinfo: content = cdinfo.read() cds = re.findall("drive name:\t\t(.*)", content) if not cds: raise InvalidParameter("KCHCDROM0003E", {'value': path}) drives = [os.path.join('/dev', p) for p in cds[0].split('\t')] if path not in drives: raise InvalidParameter("KCHCDROM0003E", {'value': path}) src_type = 'block' else: raise InvalidParameter("KCHCDROM0003E", {'value': path}) return src_type
def vmstorages_create(self, vm_name, params): path = params.get('path') if path.startswith('/') and not os.path.exists(path): raise InvalidParameter("KCHCDROM0003E", {'value': path}) dom = self._get_vm(vm_name) dev = params.get('dev', None) if dev and dev in self.vmstorages_get_list(vm_name): return OperationFailed("KCHCDROM0004E", {'dev_name': dev, 'vm_name': vm_name}) if not dev: index = len(dom.storagedevices.keys()) + 1 params['dev'] = "hd" + string.ascii_lowercase[index] vmdev = MockVMStorageDevice(params) dom.storagedevices[params['dev']] = vmdev return params['dev']
def to_volume_list(self, vm_uuid): storage_path = self._get_storage_path() ret = [] for i, d in enumerate(self.info['disks']): index = d.get('index', i) volume = "%s-%s.img" % (vm_uuid, index) info = {'name': volume, 'capacity': d['size'], 'format': d['format'], 'path': '%s/%s' % (storage_path, volume)} if 'logical' == self._get_storage_type() or \ info['format'] not in ['qcow2', 'raw']: info['allocation'] = info['capacity'] else: info['allocation'] = 0 if 'base' in d: info['base'] = dict() base_fmt = imageinfo.probe_img_info(d['base'])['format'] if base_fmt is None: raise InvalidParameter("KCHTMPL0024E", {'path': d['base']}) info['base']['path'] = d['base'] info['base']['format'] = base_fmt v_tree = E.volume(E.name(info['name'])) v_tree.append(E.allocation(str(info['allocation']), unit='G')) v_tree.append(E.capacity(str(info['capacity']), unit='G')) target_fmt = info['format'] if 'base' in d: # target must be qcow2 in order to use a backing file target_fmt = 'qcow2' v_tree.append(E.backingStore( E.path(info['base']['path']), E.format(type=info['base']['format']))) target = E.target( E.format(type=target_fmt), E.path(info['path'])) v_tree.append(target) info['xml'] = etree.tostring(v_tree) ret.append(info) return ret
def addRepo(self, repo={}): """ Add a given repository in repositories.Repositories() format to APT """ if len(repo) == 0: raise InvalidParameter("KCHREPOS0013E") if repo['url_args'] is not None: dist = repo['url_args'][0] args = repo['url_args'][1:] else: dist = None args = [] _file = '%s/%s.list' % (self._etc_sparts, repo['repo_id']) self._repos.add('deb', repo['baseurl'], dist, args, file=_file) self._repos.save()
def _create_iface_xml(self, iface, net_params): m = E.interface(E.start(mode='onboot'), type='ethernet', name=iface) if net_params['ipaddr'] and net_params['netmask']: n = ipaddr.IPv4Network( '%s/%s' % (net_params['ipaddr'], net_params['netmask'])) protocol_elem = E.protocol(E.ip(address=str(n.ip), prefix=str(n.prefixlen)), family='ipv4') if 'gateway' in net_params: protocol_elem.append((E.route(gateway=net_params['gateway']))) m.append(protocol_elem) elif net_params['ipaddr'] or net_params['netmask']: raise InvalidParameter('GINNET0012E') return ET.tostring(m)
class StorageVolumesModel(object): def __init__(self, **kargs): self.conn = kargs['conn'] self.objstore = kargs['objstore'] def create(self, pool_name, params): vol_xml = """ <volume> <name>%(name)s</name> <allocation unit="MiB">%(allocation)s</allocation> <capacity unit="MiB">%(capacity)s</capacity> <source> </source> <target> <format type='%(format)s'/> </target> </volume> """ params.setdefault('allocation', 0) params.setdefault('format', 'qcow2') name = params['name'] try: pool = StoragePoolModel.get_storagepool(pool_name, self.conn) xml = vol_xml % params except KeyError, item: raise MissingParameter("KCHVOL0004E", { 'item': str(item), 'volume': name }) pool_info = StoragePoolModel(conn=self.conn, objstore=self.objstore).lookup(pool_name) if pool_info['type'] in READONLY_POOL_TYPE: raise InvalidParameter("KCHVOL0012E", {'type': pool_info['type']}) try: pool.createXML(xml, 0) except libvirt.libvirtError as e: raise OperationFailed("KCHVOL0007E", { 'name': name, 'pool': pool, 'err': e.get_error_message() }) return name
def templates_create(self, params): name = params.get('name', '').strip() if not name: iso = params['cdrom'] iso_name = os.path.splitext(iso[iso.rfind('/') + 1:])[0] name = iso_name + str(int(time.time() * 1000)) params['name'] = name if name in self._mock_templates: raise InvalidOperation("KCHTMPL0001E", {'name': name}) for net_name in params.get(u'networks', []): try: self._get_network(net_name) except NotFoundError: msg_args = {'network': net_name, 'template': name} raise InvalidParameter("KCHTMPL0003E", msg_args) t = MockVMTemplate(params, self) self._mock_templates[name] = t return name
def create(self, vm, params): conn = self.conn.get() networks = conn.listNetworks() + conn.listDefinedNetworks() networks = map(lambda x: x.decode('utf-8'), networks) if params['type'] == 'network': network = params.get("network") if network is None: raise MissingParameter('KCHVMIF0007E') if network not in networks: raise InvalidParameter('KCHVMIF0002E', { 'name': vm, 'network': network }) macs = (iface.mac.get('address') for iface in self.get_vmifaces(vm, self.conn)) while True: params['mac'] = VMIfacesModel.random_mac() if params['mac'] not in macs: break dom = VMModel.get_vm(vm, self.conn) os_data = VMModel.vm_get_os_metadata(dom, self.caps.metadata_support) os_version, os_distro = os_data xml = get_iface_xml(params, conn.getInfo()[0], os_distro, os_version) flags = 0 if dom.isPersistent(): flags |= libvirt.VIR_DOMAIN_AFFECT_CONFIG if DOM_STATE_MAP[dom.info()[0]] != "shutoff": flags |= libvirt.VIR_DOMAIN_AFFECT_LIVE dom.attachDeviceFlags(xml, flags) return params['mac']
def create(self, vm, params): def randomMAC(): mac = [0x52, 0x54, 0x00, random.randint(0x00, 0x7f), random.randint(0x00, 0xff), random.randint(0x00, 0xff)] return ':'.join(map(lambda x: "%02x" % x, mac)) conn = self.conn.get() networks = conn.listNetworks() + conn.listDefinedNetworks() if params["type"] == "network" and params["network"] not in networks: raise InvalidParameter("KCHVMIF0002E", {'name': vm, 'network': params["network"]}) dom = VMModel.get_vm(vm, self.conn) if DOM_STATE_MAP[dom.info()[0]] != "shutoff": raise InvalidOperation("KCHVMIF0003E") macs = (iface.mac.get('address') for iface in self.get_vmifaces(vm, self.conn)) mac = randomMAC() while True: if mac not in macs: break mac = randomMAC() children = [E.mac(address=mac)] ("network" in params.keys() and children.append(E.source(network=params['network']))) ("model" in params.keys() and children.append(E.model(type=params['model']))) attrib = {"type": params["type"]} xml = etree.tostring(E.interface(*children, **attrib)) dom.attachDeviceFlags(xml, libvirt.VIR_DOMAIN_AFFECT_CURRENT) return mac
def get_list(self, _cap=None, _passthrough=None, _passthrough_affected_by=None): if _passthrough_affected_by is not None: # _passthrough_affected_by conflicts with _cap and _passthrough if (_cap, _passthrough) != (None, None): raise InvalidParameter("KCHHOST0004E") return sorted( self._get_passthrough_affected_devs(_passthrough_affected_by)) if _cap == 'fc_host': dev_names = self._get_devices_fc_host() else: dev_names = self._get_devices_with_capability(_cap) if _passthrough is not None and _passthrough.lower() == 'true': conn = self.conn.get() passthrough_names = [ dev['name'] for dev in hostdev.get_passthrough_dev_infos(conn)] dev_names = list(set(dev_names) & set(passthrough_names)) dev_names.sort() return dev_names
def _set_network_subnet(self, params): netaddr = params.get('subnet', '') # lookup a free network address for nat and isolated automatically if not netaddr: netaddr = self._get_available_address() if not netaddr: raise OperationFailed("KCHNET0009E", {'name': params['name']}) try: ip = ipaddr.IPNetwork(netaddr) except ValueError: raise InvalidParameter("KCHNET0003E", {'subent': netaddr, 'network': params['name']}) if ip.ip == ip.network: ip.ip = ip.ip + 1 dhcp_start = str(ip.ip + ip.numhosts / 2) dhcp_end = str(ip.ip + ip.numhosts - 2) params.update({'net': str(ip), 'dhcp': {'range': {'start': dhcp_start, 'end': dhcp_end}}})
def update(self, vm, mac, params): dom = VMModel.get_vm(vm, self.conn) iface = self._get_vmiface(vm, mac) if iface is None: raise NotFoundError("KCHVMIF0001E", {'name': vm, 'iface': mac}) # cannot change mac address in a running system if DOM_STATE_MAP[dom.info()[0]] != "shutoff": raise InvalidOperation('KCHVMIF0011E') # mac address is a required parameter if 'mac' not in params: raise MissingParameter('KCHVMIF0008E') # new mac address must be unique if self._get_vmiface(vm, params['mac']) is not None: raise InvalidParameter('KCHVMIF0009E', { 'name': vm, 'mac': params['mac'] }) flags = 0 if dom.isPersistent(): flags |= libvirt.VIR_DOMAIN_AFFECT_CONFIG # remove the current nic xml = etree.tostring(iface) dom.detachDeviceFlags(xml, flags=flags) # add the nic with the desired mac address iface.mac.attrib['address'] = params['mac'] xml = etree.tostring(iface) dom.attachDeviceFlags(xml, flags=flags) return [vm, params['mac']]
def _static_vm_update(self, dom, params): state = DOM_STATE_MAP[dom.info()[0]] old_xml = new_xml = dom.XMLDesc(0) for key, val in params.items(): if key in VM_STATIC_UPDATE_PARAMS: xpath = VM_STATIC_UPDATE_PARAMS[key] new_xml = xmlutils.xml_item_update(new_xml, xpath, val) try: if 'name' in params: if state == 'running': msg_args = {'name': dom.name(), 'new_name': params['name']} raise InvalidParameter("KCHVM0003E", msg_args) else: dom.undefine() conn = self.conn.get() dom = conn.defineXML(new_xml) except libvirt.libvirtError as e: dom = conn.defineXML(old_xml) raise OperationFailed("KCHVM0008E", {'name': dom.name(), 'err': e.get_error_message()}) return dom
def _passthrough_device_validate(self, dev_name): eligible_dev_names = \ DevicesModel(conn=self.conn).get_list(_passthrough='true') if dev_name not in eligible_dev_names: raise InvalidParameter('KCHVMHDEV0002E', {'dev_name': dev_name})
def create(self, params): name = params.get('name', '').strip() iso = params.get('cdrom') # check search permission if iso and iso.startswith('/') and os.path.exists(iso): st_mode = os.stat(iso).st_mode if stat.S_ISREG(st_mode) or stat.S_ISBLK(st_mode): user = UserTests().probe_user() run_setfacl_set_attr(iso, user=user) ret, excp = probe_file_permission_as_user(iso, user) if ret is False: raise InvalidParameter('KCHISO0008E', { 'filename': iso, 'user': user, 'err': excp }) cpu_info = params.get('cpu_info') if cpu_info: topology = cpu_info.get('topology') # Check, even though currently only topology # is supported. if topology: sockets = topology['sockets'] cores = topology['cores'] threads = topology['threads'] if params.get('cpus') is None: params['cpus'] = sockets * cores * threads # check_topoology will raise the appropriate # exception if a topology is invalid. CPUInfoModel(conn=self.conn).\ check_topology(params['cpus'], topology) else: params['cpu_info'] = dict() conn = self.conn.get() pool_uri = params.get(u'storagepool', '') if pool_uri: try: pool_name = pool_name_from_uri(pool_uri) pool = conn.storagePoolLookupByName(pool_name.encode("utf-8")) except Exception: raise InvalidParameter("KCHTMPL0004E", { 'pool': pool_name, 'template': name }) tmp_volumes = [ disk['volume'] for disk in params.get('disks', []) if 'volume' in disk ] self.template_volume_validate(tmp_volumes, pool) for net_name in params.get(u'networks', []): try: conn.networkLookupByName(net_name.encode('utf-8')) except Exception: raise InvalidParameter("KCHTMPL0003E", { 'network': net_name, 'template': name }) # Creates the template class with necessary information # Checkings will be done while creating this class, so any exception # will be raised here t = LibvirtVMTemplate(params, scan=True, conn=self.conn) name = params['name'] try: with self.objstore as session: if name in session.get_list('template'): raise InvalidOperation("KCHTMPL0001E", {'name': name}) session.store('template', name, t.info) except InvalidOperation: raise except Exception, e: raise OperationFailed('KCHTMPL0020E', {'err': e.message})