def detach(self, task): vm = task.get_obj('VM') vm.node.check_online(task.ignore_errors) image = task.get_obj('Image') conn = vm.node.libvirt_conn() if not vm.in_states(['stopped', 'closed']) and not task.ignore_errors: raise TaskError('vm_not_stopped') system.call([ 'scp', '%s@%s:/images/permanent-%s' % (vm.node.username, vm.node.address, vm.id), image.storage.path + '/' + image.libvirt_name + '-tmp' ]) system.call([ 'mv', image.storage.path + '/' + image.libvirt_name + '-tmp', image.storage.path + '/' + image.libvirt_name ]) image.attached_to = None image.save() for device in Device.objects.filter(object_id=image.id).all(): device.delete() try: vm.libvirt_redefine() except: pass conn.close()
def suspend(self, task): """ Suspend node to RAM for defined in config seconds. After this time + NODE_WAKEUP_TIME node is suspended again, unles it's state is not wake up. Available only in admin site or through plugins. """ node = task.get_obj('Node') if VM.objects.filter(node=node).exclude(state='closed').count() > 0: task.comment = "Node is in use. Aborting suspend" task.save() return node.set_state('suspend') node.save() log(msg="suspending node %s" % node.address, tags=('agent', 'node', 'info'), context=task.logger_ctx) system.call(['ping', '-c', '1', node.address]) arp = open('/proc/net/arp', 'r').readlines() for line in arp: fields = line.split() if fields[0] == node.address: node.set_prop('mac', fields[3]) node.save() conn = node.libvirt_conn() conn.suspendForDuration(libvirt.VIR_NODE_SUSPEND_TARGET_MEM, config.get('core', 'NODE_SUSPEND_DURATION')) conn.close()
def create(self, task): image = task.get_obj('Image') system.call('dog vdi create %s %d' % (image.libvirt_name, image.size), shell=True) image.set_state('ok') image.save()
def mk_ca(self, vpn): if not os.path.exists('/var/lib/cloudOver/coreVpn/certs/%s' % vpn.id): os.mkdir('/var/lib/cloudOver/coreVpn/certs/%s' % vpn.id) if os.path.exists(vpn.ca_key_file): raise Exception('vpn_exists') if vpn.ca_crt != '' and vpn.ca_crt != None: raise Exception('vpn_ca_exists') system.call(['openssl', 'genrsa', '-out', vpn.ca_key_file, str(config.get('vpn', 'CA_KEY_SIZE'))]) system.call(['openssl', 'req', '-x509', '-new', '-nodes', '-key', vpn.ca_key_file, '-days', str(config.get('vpn', 'CERTIFICATE_LIFETIME')), '-out', vpn.ca_crt_file, '-subj', '/CN=CoreVpn-%s/O=CloudOver/OU=CoreVpn' % vpn.id]) vpn.ca_crt = open(vpn.ca_crt_file, 'rb').read(1024*1024) vpn.save()
def save_image(self, task): node = task.get_obj('Node') node.check_online(task.ignore_errors) vm = task.get_obj('VM') image = task.get_obj('Image') if not vm.in_state('stopped'): raise TaskNotReady('vm_not_stopped') vm.set_state('saving') vm.save() system.call([ 'scp', '%s@%s:/images/%s' % (node.username, node.address, vm.id), image.storage.path + '/' + image.libvirt_name ]) vm.set_state('stopped') vm.save() image.state = long( subprocess.check_output("qemu-img info " + image.storage.path + '/' + image.libvirt_name + " | grep 'virtual size'", shell=True).split()[3][1:]) image.set_state('ok') image.save()
def wake_up(self, task): node = task.get_obj('Node') if node.has_prop('mac'): system.call(['wakeonlan', node.get_prop('mac')]) if node.in_state('suspend'): time.sleep(config.get('core', 'NODE_WAKEUP_TIME')) node.start() else: raise TaskError('Cannot find node\'s MAC')
def _sheepdog_startup(self): for i in range(30): r = system.call('dog node list', shell=True) if r == 0: break else: log(msg="sheepdog_startup: Restarting shepdog service", tags=('system', 'info')) system.call('service sheepdog restart', shell=True) time.sleep(i)
def attach_configdrive(context, userdata_id, vm_id): userdata = UserData.get(context.user_id, userdata_id) vm = VM.get(context.user_id, vm_id) system.call(['mkdir', '-p', '/tmp/configdrive_' + userdata.id + '/openstack/latest/']) ud = open('/tmp/configdrive_' + userdata.id + '/openstack/latest/user_data', 'w') ud.write(userdata.data) ud.close() # Detailed instruction: https://coreos.com/os/docs/latest/config-drive.html #TODO: Convert to: http://serverfault.com/questions/43634/how-to-mount-external-vfat-drive-as-user system.call(['genisoimage', '-R', '-V', 'config-2', '-o', '/tmp/configdrive_' + userdata.id + '.img', '/tmp/configdrive_' + userdata.id]) system.call(['rm', '-rf', '/tmp/configdrive_' + userdata.id]) system.call(['qemu-img', 'convert', '-f', 'raw', '-O', 'qcow2', '/tmp/configdrive_' + userdata.id + '.img', '/tmp/configdrive_' + userdata.id + '.qcow2']) system.call(['rm', '-rf', '/tmp/configdrive_' + userdata.id + '.img']) image = Image.create(name='CoreTalk ConfigDrive', description='UserData: %s, VM: %s' % (userdata.id, vm.id), access='private', format='qcow2', size=os.stat('/tmp/configdrive_' + userdata.id + '.qcow2').st_size, type='permanent', disk_controller='virtio', user=vm.user) image.save() create_img = Task() create_img.user_id = context.user_id create_img.type = 'image' create_img.action = 'create' create_img.append_to([vm, image, image.storage]) chunk = DataChunk() contents = open('/tmp/configdrive_' + userdata.id + '.qcow2').read(image.size) chunk.data = base64.b64encode(contents) chunk.offset = 0 chunk.image_id = image.id chunk.type = 'upload' chunk.save() upload_data = Task() upload_data.user_id = context.user_id upload_data.type = 'image' upload_data.action = 'upload_data' upload_data.set_all_props({'offset': 0, 'size': image.size, 'chunk_id': chunk.cache_key()}) upload_data.append_to([image, vm]) attach_img = Task() attach_img.user_id = context.user_id attach_img.type = 'image' attach_img.action = 'attach' attach_img.append_to([vm, image])
def create(self, task): image = task.get_obj('Image') system.call([ 'qemu-img', 'create', '-f', image.format, image.storage.path + '/' + image.libvirt_name, str(image.size) ]) image.size = os.stat(image.storage.path + '/' + image.libvirt_name).st_size image.set_state('ok') image.save()
def delete(self, task): ''' Delete base image from node ''' vm = task.get_obj('VM') vm.node.check_online(task.ignore_errors) if vm.state not in ['stopped', 'closed', 'closing'] and not task.ignore_errors: raise TaskNotReady('vm_not_stopped') system.call('dog vdi delete %s' % (vm.id), shell=True)
def delete(self, task): image = task.get_obj('Image') if image.attached_to != None and not image.attached_to.in_state( 'closed') and not task.ignore_errors: raise TaskError('image_attached') for vm in image.vm_set.all(): if not vm.in_state('closed') and not task.ignore_errors: raise TaskError('image_attached') system.call(['rm', image.storage.path + '/' + image.libvirt_name]) image.set_state('deleted') image.save()
def delete(self, task): image = task.get_obj('Image') if image.attached_to is not None and not task.ignore_errors: raise TaskError('image_attached') for vm in image.vm_set.all(): if not vm.in_state('closed') and not task.ignore_errors: raise TaskError('image_attached') system.call('dog vdi delete %s' % image.libvirt_name, shell=True) system.call('dog vdi delete %s_tmp' % image.libvirt_name, shell=True) image.set_state('deleted') image.save()
def resize_image(self, task): vm = task.get_obj('VM') vm.node.check_online(task.ignore_errors) if not vm.in_state('stopped'): raise TaskNotReady('vm_not_stopped') system.call([ 'ssh', '-l', vm.node.username, vm.node.address, 'qemu-img', 'resize' '/images/%s' % vm.id, str(task.get_prop('size')) ])
def delete(self, task): ''' Delete volume ''' node = task.get_obj('Node') node.check_online(task.ignore_errors) vm = task.get_obj('VM') if vm.state not in ['stopped', 'closed', 'closing' ] and not task.ignore_errors: raise TaskNotReady('vm_not_stopped') system.call([ 'ssh', '-l', node.username, node.address, 'rm', '/images/' + str(vm.id) ])
def upload_url(self, task): ''' Download datq from url and put its contents into given image. Operation.data should contains: - action - url - size ''' image = task.get_obj('Image') if image.attached_to != None: raise TaskError('image_attached') image.set_state('downloading') image.save() try: volume = open(image.storage.path + '/' + image.libvirt_name, 'r+') except Exception as e: raise TaskFatalError('libvirt_image_not_found', exception=e) try: remote = urllib2.urlopen(task.get_prop('url')) except Exception as e: raise TaskError('url_not_found', exception=e) bytes = 0 while bytes < int(task.get_prop('size')): data = remote.read(1024 * 250) if len(data) == 0: break volume.write(data) bytes += len(data) image = task.get_obj('Image') image.set_prop('progress', float(bytes) / float(task.get_prop('size'))) image.save() remote.close() volume.close() log(msg="Rebasing image to no backend", tags=('agent', 'image', 'info'), context=task.logger_ctx) if image.format in ['qcow2', 'qed']: r = system.call([ 'sudo', 'qemu-img', 'rebase', '-u', '-f', image.format, '-u', '-b', '', image.storage.path + '/' + image.libvirt_name ], stderr=None, stdout=None) if r != 0: image.set_state('failed') image.save() return image = task.get_obj('Image') image.size = os.stat(image.storage.path + '/' + image.libvirt_name).st_size image.set_state('ok') image.save()
def load_image(self, task): node = task.get_obj('Node') node.check_online(task.ignore_errors) vm = task.get_obj('VM') image = task.get_obj('Image') if image.state != 'ok': raise TaskNotReady('image_wrong_state') system.call([ 'scp', image.storage.path + '/' + image.libvirt_name, '%s@%s:/images/%s' % (node.username, node.address, vm.id) ]) vm.set_state('stopped') vm.save()
def attach(self, task): vm = task.get_obj('VM') vm.node.check_online(task.ignore_errors) image = task.get_obj('Image') conn = vm.node.libvirt_conn() if image.attached_to != None and not image.attached_to.in_state( 'closed'): raise TaskError('image_attached') if not vm.in_state('stopped'): raise TaskError('vm_not_stopped') if not image.in_state('ok'): raise TaskError('image_state') devices = [i.disk_dev for i in vm.image_set.all()] if 'device' in task.get_all_props().keys() and not int( task.get_prop('device')) in devices: disk_dev = int(task.get_prop('device')) else: disk_dev = 1 while disk_dev in devices: disk_dev = disk_dev + 1 image.disk_dev = disk_dev image.attached_to = vm image.save() system.call([ 'scp', image.storage.path + '/' + image.libvirt_name, '%s@%s:/images/permanent-%s' % (vm.node.username, vm.node.address, vm.id) ]) Device.create(image.id, vm, 'devices/image.xml', { 'img': image, 'disk_dev': 'sd' + chr(ord('a') + disk_dev), 'vm': vm }) vm.libvirt_redefine() conn.close()
def delete(self, task): vpn = task.get_obj('VPN') vpn.set_state('removing') vpn.save() try: pid = int(open('/var/lib/cloudOver/coreVpn/%s.pid' % vpn.id, 'rb').read(1024)) system.call(['sudo', 'kill', '-15', str(pid)]) except Exception as e: log(msg='Failed to kill openvpn process', exception=e, tags=('corevpn', 'error')) system.call('rm -rf /var/lib/cloudOver/coreVpn/certs/%s' % vpn.id, shell=True) try: os.remove('/var/lib/cloudOver/coreVpn/%s.pid' % vpn.id) except: log(msg='Failed to remove pid file', tags=('error')) vpn.set_state('removed') vpn.save()
def save_image(self, task): vm = task.get_obj('VM') vm.node.check_online(task.ignore_errors) image = task.get_obj('Image') if not vm.in_state('stopped'): raise TaskNotReady('vm_not_stopped') vm.set_state('saving') vm.save() # Save snapshot from VM (based on vm.base_image) to clone of image (operation.image) rc = None if not task.ignore_errors: rc = 0 system.call('dog vdi snapshot -s snap_%s %s' % (vm.id, vm.id), shell=True, retcode=rc) system.call('dog vdi clone -s snap_%s %s %s' % (vm.id, vm.id, image.libvirt_name), shell=True, retcode=rc) system.call('dog vdi delete -s snap_%s %s' % (vm.id, vm.id), shell=True, retcode=rc) vm.set_state('stopped') vm.save() image.set_state('ok') image.save()
def start_dhcp(self, task): network = task.get_obj('Subnet') self.stop_dhcp(task) gateway = network.get_prop('gateway', None) if gateway is None: #TODO: Exception? return system.call(['ip', 'link', 'set', network.isolated_bridge_name, 'up'], netns=network.netns_name) system.call(['ip', 'addr', 'add', 'dev', network.isolated_bridge_name, '%s/%d' % (gateway, network.mask)], netns=network.netns_name) dnsmasq = ['dnsmasq', '-O', '3', '-O', '6', '-i', network.isolated_bridge_name, '-F', '%s,%s' % (str(network.to_ipnetwork().ip+1), str(network.to_ipnetwork().broadcast-1))] for lease in network.lease_set.all(): dnsmasq.append('-G') dnsmasq.append('%s,%s' % (str(lease.mac), str(lease.address))) system.call(dnsmasq, netns=network.netns_name, background=True) network.set_prop('dhcp_running', True) network.save()
def start_dhcp(self, task): network = task.get_obj('Subnet') self.stop_dhcp(task) gateway = network.get_prop('gateway', None) if gateway is None: #TODO: Exception? return system.call(['ip', 'link', 'set', network.isolated_bridge_name, 'up'], netns=network.netns_name) system.call([ 'ip', 'addr', 'add', 'dev', network.isolated_bridge_name, '%s/%d' % (gateway, network.mask) ], netns=network.netns_name) dnsmasq = [ 'dnsmasq', '-O', '3', '-O', '6', '-i', network.isolated_bridge_name, '-F', '%s,%s' % (str(network.to_ipnetwork().ip + 1), str(network.to_ipnetwork().broadcast - 1)) ] for lease in network.lease_set.all(): dnsmasq.append('-G') dnsmasq.append('%s,%s' % (str(lease.mac), str(lease.address))) system.call(dnsmasq, netns=network.netns_name, background=True) network.set_prop('dhcp_running', True) network.save()
def mk_cert(self, vpn, name): if not os.path.exists(vpn.ca_key_file): raise Exception('vpn_root_ca_not_found') # Create key system.call(['openssl', 'genrsa', '-out', vpn.client_key_file(name), str(config.get('vpn', 'CLIENT_KEY_SIZE'))]) # Create certificate sign request system.call(['openssl', 'req', '-new', '-key', vpn.client_key_file(name), '-out', vpn.client_key_file(name) + '.csr', '-subj', '/CN=server/O=CloudOver/OU=CoreVpn/']) system.call(['openssl', 'x509', '-req', '-in', vpn.client_key_file(name) + '.csr', '-CA', vpn.ca_crt_file, '-CAkey', vpn.ca_key_file, '-CAcreateserial', '-out', vpn.client_crt_file(name), '-days', str(config.get('vpn', 'CERTIFICATE_LIFETIME'))])
def upload_data(self, task): ''' Put file given in operation.data['filename'] into given image (operation.image) at offset. The file can extend existing image. Operation.data should contain: - action - offset - filename ''' image = task.get_obj('Image') if image.attached_to != None: raise TaskError('image_attached') image.set_state('downloading') image.save() try: volume = open(image.storage.path + '/' + image.libvirt_name, 'r+') except Exception as e: raise TaskFatalError('image_not_found', exception=e) data_chunk = DataChunk(cache_key=task.get_prop('chunk_id')) data = base64.b64decode(data_chunk.data) volume.seek(data_chunk.offset) volume.write(data) volume.close() data_chunk.delete() log(msg="Rebasing image to no backend", tags=('agent', 'image', 'info'), context=task.logger_ctx) if image.format in ['qcow2', 'qed']: r = system.call([ 'sudo', 'qemu-img', 'rebase', '-u', '-f', image.format, '-u', '-b', '', image.storage.path + '/' + image.libvirt_name ], stderr=None, stdout=None) if r != 0: image = task.get_obj('Image') image.set_state('failed') image.save() return image = task.get_obj('Image') image.size = os.stat(image.storage.path + '/' + image.libvirt_name).st_size image.set_state('ok') image.save()
def load_image(self, task): image = task.get_obj('Image') vm = task.get_obj('VM') vm.node.check_online(task.ignore_errors) if image.state != 'ok': raise TaskNotReady('image_wrong_state') rc = None if not task.ignore_errors: rc = 0 system.call('dog vdi snapshot -s snap_%s %s' % (vm.id, image.libvirt_name), shell=True, retcode=rc) system.call('dog vdi clone -s snap_%s %s %s' % (vm.id, image.libvirt_name, vm.id), shell=True, retcode=rc) system.call('dog vdi delete -s snap_%s %s' % (vm.id, image.libvirt_name), shell=True, retcode=rc)
def mk_openvpn(self, vpn, network): p = system.call(['sudo', 'openvpn', '--config', vpn.config_file, '--writepid', '/var/lib/cloudOver/coreVpn/%s.pid' % vpn.id], background=True) vpn.openvpn_pid = p vpn.save() for i in range(60): r = system.call(['ip', 'link', 'show', vpn.interface_name]) if r > 0: time.sleep(1) continue else: break system.call(['sudo', 'brctl', 'addif', network.isolated_bridge_name, vpn.interface_name]) system.call(['sudo', 'ip', 'link', 'set', vpn.interface_name, 'up'])
def umount(self, task): system.call('service sheepdog stop', shell=True) storage = task.get_obj('Storage') storage.state = 'locked' storage.save()
def create_images_pool(self, task): node = task.get_obj('Node') system.call( ['ssh', '-l', node.username, node.address, 'mkdir', '/images'])
def mk_dh(self, vpn): system.call(['openssl', 'dhparam', '-out', vpn.dh_file, '1024'])
def upload_url(self, task): ''' Download datq from url and put its contents into given image. Operation.data should contains: - action - url - size ''' image = task.get_obj('Image') if image.attached_to is not None: raise TaskError('image_attached') image.set_state('downloading') image.save() try: remote = urllib2.urlopen(task.get_prop('url')) except Exception as e: raise TaskError('url_not_found', exception=e) # Create temporary object system.call('dog vdi create %s_tmp %s' % (image.libvirt_name, task.get_prop('size')), shell=True) bytes = 0 while bytes < int(task.get_prop('size')): data = remote.read(1024 * 1024) if len(data) == 0: log(msg='End of data', context=task.logger_ctx, tags=('agent', 'image', 'info')) break try: p = subprocess.Popen([ 'dog', 'vdi', 'write', image.libvirt_name + '_tmp', str(bytes) ], stdin=subprocess.PIPE) p.stdin.write(data) p.stdin.close() p.wait() except Exception as e: log(msg='Failed to finish download at %d bytes' % bytes, exception=e, context=task.logger_ctx, tags=('agent', 'image', 'error')) break bytes += len(data) image.set_prop('progress', float(bytes) / float(task.get_prop('size'))) image.save() log(msg='Converting image %s to no-backend' % (image.libvirt_name), context=task.logger_ctx, tags=('agent', 'network', 'debug')) system.call('dog vdi delete %s' % image.libvirt_name, shell=True) r = system.call([ 'qemu-img', 'convert', '-f', image.format, 'sheepdog:%s_tmp' % image.libvirt_name, '-O', 'raw', 'sheepdog:%s' % image.libvirt_name ]) if r != 0: image.set_state('failed') image.save() return system.call('dog vdi delete %s_tmp' % image.libvirt_name, shell=True) image.size = int(task.get_prop('size')) image.set_state('ok') image.save() remote.close()