def request(caller_id): """ Method requests single PublicIP address for caller. If caller's quota is exceeded, exception is raised. Otherwise caller obtains a new PublicIP address. @cmview_user @response{string} newly obtained PublicIP's address @raises{public_lease_not_found,CMException} @raises{public_lease_request,CMException} """ user = User.get(caller_id) if len(user.public_ips.all()) >= user.public_ip: raise CMException('public_lease_limit') ips = PublicIP.objects.filter(user=None).all() if len(ips) == 0: raise CMException('public_lease_not_found') ip = ips[0] ip.user = user ip.request_time = datetime.now() ip.release_time = None try: ip.save() except Exception: raise CMException('public_lease_request') return ip.address
def edit(caller_id, system_image_id, name, description, disk_controller, video_device, network_device, platform): """ Updates attributes of the specified SystemImage. @cmview_user @param_post{system_image_id,int} id of the SystemImage to edit @param_post{name,string} SystemImage new name (optional) @param_post{description,string} SystemImage new description (optional) @param_post{disk_controller} SystemImage new controller (optional) @param_post{video_device} SystemImage new video device (optional) @param_post{network_device} SystemImage new network device (optional) @param_post{platform} (optional) """ image = SystemImage.get(caller_id, system_image_id) if image.state != image_states['ok']: raise CMException('image_edit') image.name = name image.description = description image.disk_controller = disk_controller image.video_device = video_device image.network_device = network_device image.platform = platform try: image.save() except: raise CMException('image_edit')
def create(caller_id, name, description, filesystem, size, disk_controller): """ Creates new StorageImage. @cmview_user @param_post{name,string} @param_post{description,string} @param_post{filesystem,int} id of the filesystem. Supported filesystems are common.hardware.disk_filesystems @param_post{size,int} size of the SystemImage to create [MB] @param_post{disk_controller} @response{dict} StorageImage.dict property of newly created StorageImage """ if size < 1: raise CMException('image_invalid_size') user = User.get(caller_id) user.check_storage(size) image = StorageImage.create(user=user, disk_controller=disk_controller, description=description, name=name, size=size) try: image.save() except Exception, e: log.error(caller_id, "Unable to save image to DB: %s" % str(e)) raise CMException('image_create')
def set_state(self, state): """ @parameter{state,string} slug name of the new state for this Image @raises{vm_wrong_state,CMException} such a state doesn't exist """ # Key - destination state # Values - actual available states states = { 'init': (), 'adding': ('init'), 'failed': ('init', 'adding', 'formatting', 'ok', 'unavailable', 'locked', 'deleted'), 'formatting': ('adding'), 'ok': ('formatting', 'adding', 'locked'), 'unavailable': (), 'locked': ('ok'), 'deleted': ('ok', 'locked', 'failed'), } # Find my state: my_state = False for s in image_states.keys(): if self.state == image_states[s]: my_state = s if self.storage.state == storage_states['locked']: if state == 'adding' or state == 'formatting': raise CMException('vm_wrong_state') # Check if Image could go from actual state to given if not my_state in states[state] or my_state == False: raise CMException('vm_wrong_state')
def attach_vnc(self, reattach=False): if not self.state in (vm_states['init'], vm_states['running'], vm_states['running ctx']): raise CMException('vm_wrong_state') if self.vnc_enabled == vnc_states['attached'] and reattach == False: raise CMException('vm_vnc_attached') subprocess.call([ "sudo", "/sbin/iptables", "-t", "nat", "-A", "CC1_VNC_REDIRECT", "-d", settings.VNC_ADDRESS, "-p", "tcp", "--dport", str(self.vnc_port), "-j", "DNAT", "--to-destination", "%s:%s" % (self.node.address, str(self.vnc_port)) ]) subprocess.call([ "sudo", "/sbin/iptables", "-t", "nat", "-A", "CC1_VNC_MASQUERADE", "-d", self.node.address, "-p", "tcp", "--dport", str(self.vnc_port), "-j", "MASQUERADE" ]) subprocess.call([ "sudo", "/sbin/iptables", "-t", "nat", "-A", "CC1_VNC_REDIRECT", "-d", settings.VNC_ADDRESS, "-p", "tcp", "--dport", str(self.novnc_port), "-j", "DNAT", "--to-destination", "%s:%s" % (self.node.address, str(self.novnc_port)) ]) subprocess.call([ "sudo", "/sbin/iptables", "-t", "nat", "-A", "CC1_VNC_MASQUERADE", "-d", self.node.address, "-p", "tcp", "--dport", str(self.novnc_port), "-j", "MASQUERADE" ]) self.vnc_enabled = vnc_states['attached']
def install(node_id, distribution): """ Function installs node packages on node identified by node_id @parameter{node_id,int} id of the node on which packages should be installed @parameter{distribution,string} distribution of the node (what kind of packaging system should be used. Eg. 'debian' / 'redhat' @returns{int} 0, if successful @raises{CLICMException} """ try: node = Node.objects.get(id=node_id) except: raise CMException('node_not_found') public_key = open('/var/lib/cc1/.ssh/id_rsa.pub').read() # TODO: add password support r = -1 if distribution == 'debian': r = subprocess.call([ 'ssh', '-o', 'PasswordAuthentication=no', 'root@%s' % (node.address), debian_script % { 'public_key': public_key } ]) else: raise CMException('node_not_implemented') if r != 0: raise CMException('node_install') else: return 0
def download(caller_id, description, name, path, disk_dev, disk_controller): """ Downloads specified StorateImage from remote path. @cmview_admin_cm @param_post{description,string} @param_post{name,string} how to name newly downloaded storage image @param_post{path,string} HTTP or FTP path to download StorageImage. @param_post{disk_dev} @param_post{disk_controller} """ # size value is taken try: connection = urllib.urlopen(path) size = int(connection.info()["Content-Length"]) except IOError: log.exception('Cannot find image') raise CMException('image_not_found') except KeyError: log.exception(caller_id, 'Cannot calculate size') raise CMException('image_calculate_size') user = User.get(caller_id) image = StorageImage.create(name=name, description=description, user=user, disk_dev=disk_dev, disk_controller=disk_controller) try: image.save() except Exception, e: log.error(caller_id, "Unable to save image to DB: %s" % str(e)) raise CMException('image_create')
def edit(caller_id, system_image_id, name, description, disk_controller, video_device, network_device, platform): """ Sets Image's new attributes. Those should be get by src.cm.manager.image.get_by_id(). @cmview_admin_cm @param_post{system_image_id,string} new Image name @param_post{name,string} new Image name @param_post{description,string} new Image description @param_post{disk_controller} new Image controller optional @param_post{video_device} new video device optional @param_post{network_device} new network device optional @param_post{platform} optional """ image = SystemImage.admin_get(system_image_id) if image.state != image_states['ok']: raise CMException('image_edit') image.name = name image.description = description image.disk_controller = disk_controller image.video_device = video_device image.network_device = network_device image.platform = platform try: image.save() except: raise CMException('image_edit')
def get(user_id, iso_image_id): """ Method returns image \c id if it belongs to user \c user_id (and optionally to listed \c groups, if any given) @parameter{user_id,int} @parameter{iso_image_id,int} @returns{Image} image with id given @raises{image_get,CMException} """ try: image = IsoImage.objects.get(pk=iso_image_id) except: raise CMException('image_get') # check on state and storage? if image.state != image_states[ 'ok'] and image.storage.state != storage_states['ok']: raise CMException('image_unavailable') image.has_access(user_id) return image
def save_and_shutdown(farm, name, description): """ """ from cm.models.vm import VM if farm.state == farm_states['failed']: raise CMException('farm_wrong_state') head_vm = farm.head try: VM.save_and_shutdown(head_vm.user_id, head_vm, name, description) except Exception: CMException('farm_save') node_vms = [] if farm.state == farm_states['init_head']: for vm in farm.vms.all(): if vm.is_head(): continue vm.release_resources() vm.state = vm_states['closed'] else: for vm in farm.vms.all(): if not vm.is_head(): node_vms.append(vm) VM.destroy(node_vms) try: farm.state = farm_states['closed'] farm.save() except: CMException('farm_save')
def configure(node_id, interfaces): ''' interfaces - list of interfaces to communicate with cm and other nodes ''' try: node = Node.objects.get(id=node_id) except: raise CMException('node_not_found') try: sys.path.append('/etc/cc1/cm/') import config except: raise CMException('node_config_invalid') cm_ip = 'echo $SSH_CLIENT | cut -d " " -f 1' r = subprocess.call([ 'ssh', '-i', '/var/lib/cc1/.ssh/id_rsa', '%s@%s' % (node.username, node.address), 'sudo /usr/sbin/cc1_network_setup configure http://`%s`:8003/ %s %s' % (cm_ip, ','.join(interfaces), config.OSPF_TOKEN) ]) if r != 0: raise CMException('node_setup_networking') r = subprocess.call([ 'ssh', '-i', '/var/lib/cc1/.ssh/id_rsa', '%s@%s' % (node.username, node.address), 'sudo /usr/sbin/cc1_node_setup_libvirt configure %s %s %s %s %s' % (node.address, node.username, node.transport, node.driver, node.suffix) ]) if r != 0: raise CMException('node_setup_libvirt')
def attach(caller_id, iso_image_id, vm_id): # vm_id, img_id, destination='usb', check=True/False """ Attaches specified IsoImage to specified VM. It makes possible booting any operating system on created VM. @cmview_user @param_post{iso_image_id,int} id of block device (should be IsoImage type) @param_post{vm_id,int} id of the VM which IsoImage should be attached to @response{None} """ vm = VM.get(caller_id, vm_id) disk = IsoImage.get(caller_id, iso_image_id) # Check if disk is already attached to a vm if disk.vm: raise CMException('image_attached') disk.attach(vm) try: disk.save() except: raise CMException('iso_image_attach')
def add(caller_id, address, mask): networks = [] for net in AvailableNetwork.objects.all(): networks.append(net.to_ipnetwork()) networks.sort() # Find duplicate ipnet = IPNetwork('%s/%d' % (address, mask)) for i in xrange(len(networks)): if ipnet.prefixlen > networks[i].prefixlen and ipnet > networks[ i].previous() and ipnet < networks[i].next(): raise CMException('network_exists') elif ipnet.prefixlen < networks[i].prefixlen and ipnet.previous( ) < networks[i] and ipnet.next() > networks[i]: raise CMException('network_exists') # Add new network new_net = AvailableNetwork() new_net.address = ipnet.network new_net.mask = mask if ipnet.is_private(): new_net.state = available_network_states['ok'] else: new_net.state = available_network_states['locked'] new_net.save()
def get_unused_ipnetwork(self, mask): """ @returns Unused subnetwork represented by IPNetwork object """ networks = [] for network in self.usernetwork_set.all(): networks.append(network.to_ipnetwork()) networks = sorted(networks) log.debug(1, 'Networks: %s' % str(networks)) if self.mask > mask: raise CMException('network_to_large') if len(networks) == 0: return IPNetwork(self.address + '/' + str(mask)) if IPNetwork(self.address + '/' + str(mask)).network < networks[0].network: return IPNetwork(self.address + '/' + str(mask)) # Find matching hole in existing networks for i in xrange(len(networks) - 1): n = IPNetwork(str(networks[i].next().ip) + "/" + str(mask)) if networks[i] < n and n < networks[i + 1]: return n # If previous fails, try to fit network at end of pool n = IPNetwork(str(networks[-1].next().network) + "/" + str(mask)) log.debug(1, 'Trying: %s' % str(n)) if networks[-1].network < n.network and n.network < self.to_ipnetwork().next().network: return n else: # or raise exception, if this is not possible raise CMException("network_unavailable")
def create(caller_id, name, address, directory, capacity): """ Registers new Storage. @cmview_admin_cm @param_post{name,string} libvirt's pool name @param_post{address,string} storage ip address or hostname @param_post{directory,string} directory on storage @param_post{capacity,int} maximum storage capacity [MB] @raises{storage_already_exist,CMException} @raises{storage_create,CMException} """ #error if already exists a storage with the same name if Storage.objects.filter(name__exact=name).exists(): raise CMException('storage_already_exist') try: st = Storage() st.name = name st.address = address st.dir = directory st.capacity = capacity st.state = storage_states['ok'] except Exception, e: log.debug(caller_id, 'Cannot register storage - missing element: %s' % str(e)) raise CMException('storage_create')
def check_quota(self, template_count): """ @todo Test template_count is a list of template objects? Method checks this User's quota for ability to run VMs based on template given and it raises CMException, if it's exceeded: @parameter{template_count} @raises{user_cpu_limit,CMException} @raises{user_memory_limit,CMException} @raises{user_storage_limit,CMException} @raises{user_points_limit,CMException} """ cpu_sum = 0 mem_sum = 0 for template, count in template_count: cpu_sum += template.cpu * count mem_sum += template.memory * count if self.used_cpu + cpu_sum > self.cpu: raise CMException('user_cpu_limit') if self.used_memory + mem_sum > self.memory: raise CMException('user_memory_limit') if self.used_storage > self.storage: raise CMException('user_storage_limit')
def assign(self, lease): if lease.vm == None: raise CMException('lease_not_attached') self.lease = lease self.save() log.debug( 0, "Attaching ip with comand: %s" % str([ 'ssh', '-i', '/var/lib/cc1/.ssh/id_rsa', '%s@%s' % (lease.vm.node.username, lease.vm.node.address), 'sudo /usr/sbin/cc1_node_public_ip attach %d %s %s' % (lease.vm.id, lease.vm_address, self.address) ])) p = subprocess.Popen([ 'ssh', '-i', '/var/lib/cc1/.ssh/id_rsa', '%s@%s' % (lease.vm.node.username, lease.vm.node.address), 'sudo /usr/sbin/cc1_node_public_ip attach %d %s %s' % (lease.vm.id, lease.vm_address, self.address) ], stdout=subprocess.PIPE) p.wait() log.debug(self.user.id, p.stdout.read()) if p.returncode != 0: log.error(self.user.id, "SSH error: %d" % p.returncode) raise CMException('public_ip_failed')
def destroy(farms): """ Destroyes farms' VMs (Head and Worker Nodes of each farm) without saving them. @parameter{farms,list} list of farms to destroy @response{list(dict)} list of statuses returned by destroyed VMs @raises{farm_wrong_state,CMException} @raises{farm_destroy,CMException} """ from cm.models.vm import VM vm_resp = [] for farm in farms: # those are states in which farm can not be destroyed if farm.state in (farm_states['init'], farm_states['closing'], farm_states['closed']): raise CMException('farm_wrong_state') for farm in farms: # stop all threads if farm.state == farm_states['init_head']: for vm in farm.vms.all(): if vm.is_head(): continue vm.release_resources() vm.state = vm_states['closed'] vm.stop_time = datetime.now() vm.save() log.debug(vm.user.id, "vm state %s" % vm.state) r = VM.destroy([farm.head]) else: for vm in farm.vms.all(): if vm.state == vm_states['init']: raise CMException('farm_wrong_state') log.debug(farm.user_id, "killing wn: %s" % farm.vms) r = VM.destroy(farm.vms.all()) if True in [x['status'] != 'ok' for x in r]: farm.state = farm_states['failed'] try: farm.save() except Exception: raise CMException('farm_destroy') vm_resp.append(r) farm.state = farm_states['closed'] try: farm.save() except Exception: raise CMException('farm_destroy') log.debug(farm.user_id, "session commited") for vm in farm.vms.all(): log.debug(vm.user.id, "vm state %s" % vm.state) return vm_resp
def get(user_id, farm_id): try: farm = Farm.objects.get(pk=farm_id) except: raise CMException('vm_get') if farm.user.id != user_id: raise CMException('user_permission') return farm
def detach_node(self): """ @raises{lease_detached,CMException} Network was not defined """ if self.vm_id == None: raise CMException('lease_detached') # Destroy network try: conn = libvirt.open(self.vm.node.conn_string) except Exception, e: log.exception(self.user_network.user_id, "Cannot connet to libvirt: ") raise CMException('lease_detach')
def attach(self, vm): """ Attaches this StorageImage to specified VM. It searches for first free device and if there's any, tries to attach this to it via Libvirt. Further it updates DB information. @parameter{vm,VM} instance of the existing VM @raises{storage_image_attach,CMException} no free device found or cannot attach StorageImage """ domain = vm.lv_domain() log.debug(self.user.id, self.disk_controller) # Get all block devices and find first, unused sdX # attached_devices = [d.disk_dev for d in Session.query(StorageImage).filter(StorageImage.vm_id == vm.id).all()] attached_devices = [ d.disk_dev for d in StorageImage.objects.filter(vm_id__exact=vm.id) ] free_dev = None # find the first free numbers to be given to disk volume (sda is now integer) for i in range(2, 12): if not i in attached_devices: free_dev = i break if free_dev == None: raise CMException('storage_image_attach') try: device_desc = """<disk type='file' device='disk'> <driver name='qemu' type='raw'/> <source file='%(path)s'/> <target dev='%(dev)s' bus='%(bus)s'/> <alias name='%(bus)s-%(dev)s'/> </disk>""" % { 'path': self.path, # disk_dev name will be in format sd+letter corresponding to the number (e.g: 2->sdb) 'dev': 'sd%s' % chr(free_dev + 98), 'bus': self.disk_controller_name } log.debug(self.user.id, device_desc) domain.attachDevice(device_desc) except: log.exception(self.user.id, 'storage attach') raise CMException('storage_image_attach') # Update database information self.disk_dev = free_dev self.vm = vm
def delete(caller_id, iso_image_id): """ Deletes specified IsoImage. @cmview_user @param_post{iso_image_ids} id of the IsoImage to delete """ image = IsoImage.get(caller_id, iso_image_id) if image.state != image_states['ok']: raise CMException('image_delete') try: subprocess.call(['rm', image.path]) except Exception, e: raise CMException('image_delete')
def admin_get(farm_id): try: farm = Farm.objects.get(pk=farm_id) except: raise CMException('vm_get') return farm
def edit(caller_id, template_id, name, memory, cpu, description, points, ec2name): """ Updates specified Template's attributes. @cmview_admin_cm @param_post{template_id,int} id of the Template to edit @param_post{name,string} @param_post{memory,int} @param_post{cpu,int} @param_post{description,string} @param_post{points,int} @param_post{ec2name,int} """ try: template = Template.objects.get(pk=template_id) template.name = name template.memory = memory template.cpu = cpu template.description = description template.points = points template.ec2name = ec2name template.save() except: raise CMException('template_edit')
def add(caller_id, name, memory, cpu, description, points, ec2name): """ Creates and saves new VM Template. @cmview_admin_cm @param_post{name,string} @param_post{memory,int} @param_post{cpu,int} @param_post{description,string} @param_post{points,int} @param_post{ec2name,string} name for EC2 interface """ try: template = Template() template.name = name template.memory = memory template.cpu = cpu template.description = description template.points = points template.ec2name = ec2name template.state = template_states['active'] template.save() except: raise CMException('template_create')
def check_points(self): """ Check if used points is over the limit It raises exception in that case. """ if self.used_points >= self.points: raise CMException('user_points_limit')
def multiple_change_quota(caller_id, user_ids, memory=None, cpu=None, storage=None, public_ip=None, points=None): """ Changes quota of multiple users. @cmview_admin_cm @param_post{user_ids,list(int)} @param_post{memory,int} new RAM memory limit [MB] @param_post{cpu,int} new CPU's limit @param_post{storage,int} new storage space's limit [MB] @param_post{public_ip,int} new limit of public_ips @param_post{points,int} new monthly points limit """ for user_id in user_ids: user = User.get(user_id) user.memory = memory or user.memory user.cpu = cpu or user.cpu user.storage = storage or user.storage user.public_ip = public_ip or user.public_ip user.points = points or user.points try: user.save() except: raise CMException('user_change_quota')
def list_user_networks(caller_id, user_id=None): """ @cmview_admin_cm @param_post{user_id,int} (optional) if specified, only networks belonging to specigied User are fetched. @param_post{only_unused,bool} (optional) if @val{True}, only unused networks are returned @response{list(dict)} UserNetwork.dict property for each requested UserNetwork """ try: user_networks = [] if user_id: user = User.get(user_id) user_networks = UserNetwork.objects.filter(user=user) else: user_networks = UserNetwork.objects.all() except: raise CMException('network_not_found') response = [] for network in user_networks: response.append(network.dict) return response
def detach(self, vm): """ Requests Libvirt to detach from given VM this StorageImage. @parameter{vm,VM} VM from which StorageImage should be detached. @raises{storage_image_detach,CMException} cannot detach StorageImage """ domain = vm.lv_domain() try: device_desc = """<disk type='file' device='disk'> <driver name='qemu' type='raw'/> <source file='%(path)s'/> <target dev='%(dev)s' bus='%(bus)s'/> <alias name='%(bus)s-%(dev)s'/> </disk>""" % { 'path': self.path, 'dev': 'sd%s' % chr(self.disk_dev + 98), 'bus': self.disk_controller_name } domain.detachDevice(device_desc) except: log.exception(self.user.id, 'storage detach') raise CMException('storage_image_detach') self.vm = None
def prepare_temporary_pool_xml(self, image): """ Create temporary Libvirt Pool description to copy from/to images. storage and user parameters are used to define storage path @raises{cm_template_create,CMException} """ try: django_settings.configure() except Exception: pass try: # Open template file template = open("%s/storage_dir.xml" % settings.TEMPLATE_DIR).read() # Create django template st_template = loader.get_template_from_string(template) c = Context({ 'parent_pool': image.storage.name, 'user': image.user.id, 'cc_userid': 331, 'cc_groupid': 331 }) t = st_template.render(c) log.info(self.user.id, "Rendered template: %s" % t) except Exception, e: log.exception(self.user.id, "Cannot create template: %s" % str(e)) raise CMException('cm_template_create')