diff -Nru archipel-agent-vmparking-0.5.0/archipelagentvmparking/vmparking.py archipel-agent-vmparking-0.6.0/archipelagentvmparking/vmparking.py --- archipel-agent-vmparking-0.5.0/archipelagentvmparking/vmparking.py 2012-11-02 13:54:29.000000000 +0000 +++ archipel-agent-vmparking-0.6.0/archipelagentvmparking/vmparking.py 2013-03-21 00:58:52.000000000 +0000 @@ -21,23 +21,30 @@ import datetime import os +import random import shutil +import sqlite3 +import string import xmpp from archipel.archipelHypervisor import TNArchipelHypervisor from archipel.archipelVirtualMachine import TNArchipelVirtualMachine from archipelcore.archipelPlugin import TNArchipelPlugin -from archipelcore.pubsub import TNPubSubNode from archipelcore.utils import build_error_iq, build_error_message -ARCHIPEL_ERROR_CODE_VMPARK_LIST = -11001 -ARCHIPEL_ERROR_CODE_VMPARK_PARK = -11002 -ARCHIPEL_ERROR_CODE_VMPARK_UNPARK = -11003 -ARCHIPEL_ERROR_CODE_VMPARK_DELETE = -11004 -ARCHIPEL_ERROR_CODE_VMPARK_UPDATEXML = -11004 +ARCHIPEL_ERROR_CODE_VMPARK_LIST = -11001 +ARCHIPEL_ERROR_CODE_VMPARK_PARK = -11002 +ARCHIPEL_ERROR_CODE_VMPARK_UNPARK = -11003 +ARCHIPEL_ERROR_CODE_VMPARK_DELETE = -11004 +ARCHIPEL_ERROR_CODE_VMPARK_UPDATEXML = -11004 +ARCHIPEL_ERROR_CODE_VMPARK_CREATE_PARKED = -11005 ARCHIPEL_NS_HYPERVISOR_VMPARKING = "archipel:hypervisor:vmparking" -ARCHIPEL_NS_VM_VMPARKING = "archipel:vm:vmparking" +ARCHIPEL_NS_VM_VMPARKING = "archipel:vm:vmparking" + +ARCHIPEL_PARKING_STATUS_NOT_PARKED = 0 +ARCHIPEL_PARKING_STATUS_PARKED = 1 +ARCHIPEL_PARKING_STATUS_STATE_CHANGING = 2 class TNVMParking (TNArchipelPlugin): @@ -52,8 +59,6 @@ @param entry_point_group: the group name of plugin entry_point """ TNArchipelPlugin.__init__(self, configuration=configuration, entity=entity, entry_point_group=entry_point_group) - self.pubsub_vmparking = None; - self.inhibit_next_general_push = None # creates permissions self.entity.permission_center.create_permission("vmparking_park", "Authorizes user to park a virtual machines", False) @@ -63,6 +68,7 @@ self.entity.permission_center.create_permission("vmparking_unpark", "Authorizes user to unpark a virtual machines", False) self.entity.permission_center.create_permission("vmparking_delete", "Authorizes user to delete parked virtual machines", False) self.entity.permission_center.create_permission("vmparking_updatexml", "Authorizes user to delete parked virtual machines", False) + self.entity.permission_center.create_permission("vmparking_create_parked", "Authorizes user to create a new VM in parking", False) # vocabulary if isinstance(self.entity, TNArchipelHypervisor): @@ -94,10 +100,39 @@ self.entity.add_message_registrar_items(registrar_items) - # register to the node vmrequest + # register to the node parking and create database if needed if isinstance(self.entity, TNArchipelHypervisor): - self.entity.register_hook("HOOK_ARCHIPELENTITY_XMPP_AUTHENTICATED", method=self.manage_vmparking_node) + self.manage_database() + + if self.entity.__class__.__name__ == "TNArchipelVirtualMachine": + self.entity.register_hook("HOOK_VM_DEFINE", method=self.hook_vm_define) + self.entity.register_hook("HOOK_VM_UNDEFINE", method=self.hook_vm_undefine) + ### Hooks + def hook_vm_define(self, origin=None, user_info=None, arguments=None): + """ + Called when a VM definition occurs. + This will insert the XML desc in the parking + """ + hypervisor_parking_plugin = self.entity.hypervisor.get_plugin("vmparking") + xmldesc = self.entity.xmldesc(mask_description=False) + + if not hypervisor_parking_plugin.is_vm_registered(self.entity.uuid): + vms_info = [{"uuid": self.entity.uuid, "domain": str(xmldesc), "parker": "nobody", "status": ARCHIPEL_PARKING_STATUS_NOT_PARKED, "creation_date": datetime.datetime.now()}] + hypervisor_parking_plugin.register_vms_into_db(vms_info) + else: + hypervisor_parking_plugin.update_vm_domain_in_db(self.entity.uuid, xmldesc) + + def hook_vm_undefine(self, origin=None, user_info=None, arguments=None): + """ + Called when a VM definition occurs. + This will insert the XML desc in the parking + """ + hypervisor_parking_plugin = self.entity.hypervisor.get_plugin("vmparking") + + if hypervisor_parking_plugin.is_vm_registered(self.entity.uuid): + if not hypervisor_parking_plugin.is_vm_parked(self.entity.uuid): + hypervisor_parking_plugin.unregister_vms_from_db([{"uuid": self.entity.uuid}]) ### Plugin interface @@ -121,76 +156,50 @@ self.entity.xmppclient.UnregisterHandler('iq', self.process_iq_for_vm, ns=ARCHIPEL_NS_VM_VMPARKING) - @staticmethod - def plugin_info(): + ### Database Management + + def manage_database(self): """ - Return informations about the plugin. - @rtype: dict - @return: dictionary contaning plugin informations + Create and / or recover the parking database """ - plugin_friendly_name = "Virtual Machine Parking" - plugin_identifier = "vmparking" - plugin_configuration_section = None - plugin_configuration_tokens = [] - return { "common-name" : plugin_friendly_name, - "identifier" : plugin_identifier, - "configuration-section" : plugin_configuration_section, - "configuration-tokens" : plugin_configuration_tokens } - - + self.database = sqlite3.connect(self.configuration.get("VMPARKING", "database"), check_same_thread=False) + self.database.row_factory = sqlite3.Row + self.database.execute("create table if not exists parking (uuid text unique, parker string, creation_date date, domain string, status int)") + self.database.commit() - ### Pubsub management - - def manage_vmparking_node(self, origin, user_info, arguments): + def register_vms_into_db(self, vm_informations): """ - Register to pubsub event node /archipel/platform/requests/in - and /archipel/platform/requests/out - @type origin: L{TNArchipelEnity} - @param origin: the origin of the hook - @type user_info: object - @param user_info: random user information - @type arguments: object - @param arguments: runtime argument + Add a VM in the parking + @type vm_informations: list + @param vm_informations: list of dict containing {"uuid": x, "parker": y, "creation_date": z, "status": s} """ - nodeVMParkingName = "/archipel/vmparking" - self.entity.log.info("VMPARKING: getting the pubsub node %s" % nodeVMParkingName) - self.pubsub_vmparking = TNPubSubNode(self.entity.xmppclient, self.entity.pubsubserver, nodeVMParkingName) - self.pubsub_vmparking.recover(wait=True) - self.entity.log.info("VMPARKING: node %s recovered." % nodeVMParkingName) - self.pubsub_vmparking.subscribe(self.entity.jid, self._handle_request_event, wait=True) - self.entity.log.info("VMPARKING: entity %s is now subscribed to events from node %s" % (self.entity.jid, nodeVMParkingName)) + self.database.executemany("insert into parking values(:uuid, :parker, :creation_date, :domain, :status)", vm_informations) + self.database.commit() - def _handle_request_event(self, event): + def unregister_vms_from_db(self, vms_uuid): """ - Triggered when a platform wide virtual machine request is received. - @type event: xmpp.Node - @param event: the push event + Add a VM in the parking + @type vm_informations: list + @param vm_informations: list of dict containing {"uuid": x} """ - self.entity.log.debug("VMPARKING: received pubsub event") - if not self.inhibit_next_general_push: - self.entity.push_change("vmparking", "external-update") - self.inhibit_next_general_push = False - - + self.database.executemany("delete from parking where uuid=:uuid", vms_uuid) + self.database.commit() - ### Utilities - - def get_ticket_from_uuid(self, uuid): + def is_vm_registered(self, uuid): """ - parse the parked vm to find the ticket of the given uuid + Check if vm with given UUID is already in DB @type uuid: String @param uuid: the UUID of the vm - @rtype: String - @return: pubsub item id + @rtype: Boolean + @return: True is vm is already in park """ - items = self.pubsub_vmparking.get_items() - for item in items: - domain = item.getTag("virtualmachine").getTag("domain") - if domain.getTag("uuid").getData() == uuid: - return item.getAttr("id") - return None + rows = self.database.execute("select uuid from parking where uuid=?", (uuid,)) + n = rows.fetchone() + if n and n[0] > 1: + return True + return False - def is_vm_already_parked(self, uuid): + def is_vm_parked(self, uuid): """ Check if vm with given UUID is already parked @type uuid: String @@ -198,10 +207,75 @@ @rtype: Boolean @return: True is vm is already in park """ - if self.get_ticket_from_uuid(uuid): + rows = self.database.execute("select uuid from parking where uuid=? and status=?", (uuid, ARCHIPEL_PARKING_STATUS_PARKED)) + n = rows.fetchone() + if n and n[0] > 1: return True return False + def get_vm_by_uuid_from_db(self, uuid): + """ + Get a VM from the parking + @type uuid: String + @param uuid: The UUID of the VM + @rtype: dict + @return: dict like {"uuid": x, "parker": y, "date": z, "status": s, "domain": d} + """ + rows = self.database.execute("select * from parking where uuid=?", (uuid,)) + if not rows: + return None + row = rows.fetchone() + return {"uuid": row[0], "parker": row[1], "date": row[2], "status": row[4], "domain": xmpp.simplexml.NodeBuilder(data=row[3]).getDom()} + + def get_all_vms_from_db(self): + """ + Return all vms in parkings + @rtype: list + @return: list containing dict like {"uuid": x, "parker": y, "date": z, "status": s, "domain": d} + """ + rows = self.database.execute("select * from parking where status=?", (ARCHIPEL_PARKING_STATUS_PARKED,)) + ret = [] + for row in rows: + ret.append({"uuid": row[0], "parker": row[1], "date": row[2], "status": row[4], "domain": xmpp.simplexml.NodeBuilder(data=row[3]).getDom()}) + return ret + + def update_vm_domain_in_db(self, uuid, new_domain): + """ + Update the domain of a parked virtual machine + @type uuid: string + @param uuid: the UUID of the parked VM to update + """ + self.database.execute("update parking set domain=? where uuid=?", (str(new_domain).replace('xmlns=\"archipel:hypervisor:vmparking\"', ''), uuid)) + self.database.commit() + + def set_vms_status(self, vm_informations): + """ + Set the status of the parking + @type vm_informations: list + @param vm_informations: list of dict containing {"uuid": x, "parker": y, "status": z} + """ + self.database.executemany("update parking set status=:status, parker=:parker where uuid=:uuid", vm_informations) + self.database.commit() + + + ### Plugin information + + @staticmethod + def plugin_info(): + """ + Return informations about the plugin. + @rtype: dict + @return: dictionary contaning plugin informations + """ + plugin_friendly_name = "Virtual Machine Parking" + plugin_identifier = "vmparking" + plugin_configuration_section = "VMPARKING" + plugin_configuration_tokens = ["database"] + return {"common-name": plugin_friendly_name, + "identifier": plugin_identifier, + "configuration-section": plugin_configuration_section, + "configuration-tokens": plugin_configuration_tokens} + ### Processing function @@ -217,146 +291,125 @@ @rtype: Array @return: listinformations about virtual machines. """ - nodes = self.pubsub_vmparking.get_items() + vms = self.get_all_vms_from_db() ret = [] - for node in nodes: - domain = xmpp.Node(node=node.getTag("virtualmachine").getTag("domain")) - ret.append({"info": - {"itemid": node.getAttr("id"), - "parker": node.getTag("virtualmachine").getAttr("parker"), - "date": node.getTag("virtualmachine").getAttr("date")}, - "domain": domain}) + for vm in vms: + ret.append({"info": {"uuid": vm["uuid"], "parker": vm["parker"], "date": vm["date"]}, "domain": vm["domain"]}) + def sorting(a, b): return cmp(a["domain"].getTag("name").getData(), b["domain"].getTag("name").getData()) + ret.sort(sorting) return ret - def park(self, uuid, parker_jid, force=False): + def park(self, vm_informations): """ Park a virtual machine - @type uuid: String - @param uuid: the UUID of the virtual machine to park - @type force: Boolean - @param force: if True, the machine will be destroyed if running - """ - if self.is_vm_already_parked(uuid): - raise Exception("VM with UUID %s is already parked" % uuid) - - vm = self.entity.get_vm_by_uuid(uuid) - if not vm: - raise Exception("No virtual machine with UUID %s" % uuid) - if not vm.domain: - raise Exception("VM with UUID %s cannot be parked because it is not defined" % uuid) - if not vm.info()["state"] == 5: - if not force: - raise Exception("VM with UUID %s cannot be parked because it is running" % uuid) - else: + @type vm_informations: list + @param vm_informations: list of dict like {"uuid": x, "status": y, "parker": z)} + """ + vm_informations_cleaned = [] + for vm_info in vm_informations: + if self.is_vm_parked(vm_info["uuid"]): + self.entity.log.error("VMPARKING: VM with UUID %s is already parked" % vm_info["uuid"]) + continue + + vm = self.entity.get_vm_by_uuid(vm_info["uuid"]) + if not vm: + self.entity.log.error("VMPARKING: No virtual machine with UUID %s" % vm_info["uuid"]) + continue + if not vm.domain: + self.entity.log.error("VMPARKING: VM with UUID %s cannot be parked because it is not defined" % vm_info["uuid"]) + continue + vm_informations_cleaned.append(vm_info) + + # Now, perform operations + for vm_info in vm_informations_cleaned: + vm = self.entity.get_vm_by_uuid(vm_info["uuid"]) + if not vm.info()["state"] == 5: vm.destroy() + domain = vm.xmldesc(mask_description=False) + vm_jid = xmpp.JID(domain.getTag("description").getData().split("::::")[0]) + self.set_vms_status([vm_info]) + self.entity.soft_free(vm_jid) + self.entity.push_change("vmparking", "parked") - domain = vm.xmldesc(mask_description=False) - vm_jid = xmpp.JID(domain.getTag("description").getData().split("::::")[0]) - - def publish_success(resp): - if resp.getType() == "result": - self.entity.soft_free(vm_jid) - self.inhibit_next_general_push = True - self.entity.push_change("vmparking", "parked") - self.entity.log.info("VMPARKING: successfully parked %s" % str(vm_jid)) - else: - self.inhibit_next_general_push = True - self.entity.push_change("vmparking", "cannot-park", content_node=resp) - self.entity.log.error("VMPARKING: cannot park: %s" % str(resp)) - - vmparkednode = xmpp.Node(tag="virtualmachine", attrs={ "parker": parker_jid.getStripped(), - "date": datetime.datetime.now(), - "origin": self.entity.jid.getStripped().lower()}) - vmparkednode.addChild(node=domain) - self.pubsub_vmparking.add_item(vmparkednode, callback=publish_success) - self.entity.log.info("VMPARKING: virtual machine %s as been parked" % uuid) - - def unpark(self, identifier, start=False): + def unpark(self, vm_informations): """ Unpark virtual machine - @type identifier: String - @param identifier: the UUID of a VM or the pubsub ID (parking ticket) - @type start: Boolean - @param start: if True, the virtual machine will start after unparking - """ - ticket = self.get_ticket_from_uuid(identifier) - if not ticket: - ticket = identifier - vm_item = self.pubsub_vmparking.get_item(ticket) - if not vm_item: - raise Exception("There is no virtual machine parked with ticket %s" % ticket) - - def retract_success(resp, user_info): - if resp.getType() == "result": - domain = vm_item.getTag("virtualmachine").getTag("domain") - ret = str(domain).replace('xmlns=\"archipel:hypervisor:vmparking\"', '') - domain = xmpp.simplexml.NodeBuilder(data=ret).getDom() - vmjid = domain.getTag("description").getData().split("::::")[0] - vmpass = domain.getTag("description").getData().split("::::")[1] - vmname = domain.getTag("name").getData() - vm_thread = self.entity.soft_alloc(xmpp.JID(vmjid), vmname, vmpass, start=False) - vm = vm_thread.get_instance() - vm.register_hook("HOOK_ARCHIPELENTITY_XMPP_AUTHENTICATED", method=vm.define_hook, user_info=domain, oneshot=True) - if start: - vm.register_hook("HOOK_ARCHIPELENTITY_XMPP_AUTHENTICATED", method=vm.control_create_hook, oneshot=True) - vm_thread.start() - self.inhibit_next_general_push = True - self.entity.push_change("vmparking", "unparked") - self.entity.log.info("VMPARKING: successfully unparked %s" % str(vmjid)) + @type vm_informations: list + @param vm_informations: list of dict like {"uuid": x, "status": y, "start": True|False, "parker": z} + """ + vm_informations_cleaned = [] + # First, check if everything is correct and cleanup bad items + for vm_info in vm_informations: + if self.is_vm_parked(vm_info["uuid"]): + vm_informations_cleaned.append(vm_info) else: - self.inhibit_next_general_push = True - self.entity.push_change("vmparking", "cannot-unpark", content_node=resp) - self.entity.log.error("VMPARKING: cannot unpark: %s" % str(resp)) - self.pubsub_vmparking.remove_item(ticket, callback=retract_success) + self.entity.log.error("VMPARKING: There is no virtual machine parked with uuid %s" % vm_info["uuid"]) + + # Now, perform operations + for vm_info in vm_informations_cleaned: + vm_item = self.get_vm_by_uuid_from_db(vm_info["uuid"]) + domain = vm_item["domain"] + ret = str(domain).replace('xmlns=\"archipel:hypervisor:vmparking\"', '') + domain = xmpp.simplexml.NodeBuilder(data=ret).getDom() + vmjid = domain.getTag("description").getData().split("::::")[0] + vmpass = domain.getTag("description").getData().split("::::")[1] + vmname = domain.getTag("name").getData() + vm_thread = self.entity.soft_alloc(xmpp.JID(vmjid), vmname, vmpass, start=False, organizationInfo=self.entity.vcard_infos) + vm = vm_thread.get_instance() + vm.register_hook("HOOK_ARCHIPELENTITY_XMPP_AUTHENTICATED", method=vm.define_hook, user_info=domain, oneshot=True) + if vm_info["start"]: + vm.register_hook("HOOK_ARCHIPELENTITY_XMPP_AUTHENTICATED", method=vm.control_create_hook, oneshot=True) + vm_thread.start() + self.set_vms_status([vm_info]) + + self.entity.push_change("vmparking", "unparked") + self.entity.log.info("VMPARKING: successfully unparked %s" % str(vmjid)) - def delete(self, identifier): + def delete(self, vm_uuids): """ Delete a parked virtual machine - @type identifier: String - @param identifier: the UUID of a parked VM or the pubsub ID (parking ticket) + @type vm_uuids: list + @param uuid: list of dic like {"uuid": x} """ - ticket = self.get_ticket_from_uuid(identifier) - if not ticket: - ticket = identifier - vm_item = self.pubsub_vmparking.get_item(ticket) - if not vm_item: - raise Exception("There is no virtual machine parked with ticket %s" % ticket) - - def retract_success(resp, user_info): - if resp.getType() == "result": - vmjid = xmpp.JID(vm_item.getTag("virtualmachine").getTag("domain").getTag("description").getData().split("::::")[0]) - vmfolder = "%s/%s" % (self.configuration.get("VIRTUALMACHINE", "vm_base_path"), vmjid.getNode()) - if os.path.exists(vmfolder): - shutil.rmtree(vmfolder) - self.entity.get_plugin("xmppserver").users_unregister([vmjid]) - self.inhibit_next_general_push = True - self.entity.push_change("vmparking", "deleted") - self.entity.log.info("VMPARKING: successfully deleted %s from parking" % str(vmjid)) - else: - self.inhibit_next_general_push = True - self.entity.push_change("vmparking", "cannot-delete", content_node=resp) - self.entity.log.error("VMPARKING: cannot delete: %s" % str(resp)) - self.pubsub_vmparking.remove_item(ticket, callback=retract_success) + vm_jids = [] + # first, check there is no problems + for vm_uuid in vm_uuids: + if not self.is_vm_parked(vm_uuid["uuid"]): + raise Exception("There is no virtual machine parked with uuid %s" % vm_uuid["uuid"]) + vm_item = self.get_vm_by_uuid_from_db(vm_uuid["uuid"]) + vm_jids.append(xmpp.JID(vm_item["domain"].getTag("description").getData().split("::::")[0])) + + # Update DB and Push + self.unregister_vms_from_db(vm_uuids) + self.entity.push_change("vmparking", "deleted") + + # Then perfom cleanup operations + for vm_jid in vm_jids: + vmfolder = "%s/%s" % (self.configuration.get("VIRTUALMACHINE", "vm_base_path"), vm_jid.getNode()) + if os.path.exists(vmfolder): + shutil.rmtree(vmfolder) + + # And remove the XMPP account + self.entity.get_plugin("xmppserver").users_unregister(vm_jids) + self.entity.log.info("VMPARKING: successfully deleted %s from parking" % str(vm_jids)) - def updatexml(self, identifier, domain): + def updatexml(self, uuid, domain): """ Update the domain XML of a parked VM - @type identifier: String - @param identifier: the pubsub ID (parking ticket) or the VM UUID + @type uuid: String + @param uuid: the VM UUID @type domain: xmpp.Node @param domain: the new XML description """ - ticket = self.get_ticket_from_uuid(identifier) - if not ticket: - ticket = identifier - vm_item = self.pubsub_vmparking.get_item(ticket) - if not vm_item: - raise Exception("There is no virtual machine parked with ticket %s" % ticket) + if not self.is_vm_parked(uuid): + raise Exception("There is no virtual machine parked with uuid %s" % uuid) + + vm_item = self.get_vm_by_uuid_from_db(uuid) - old_domain = vm_item.getTag("virtualmachine").getTag("domain") + old_domain = vm_item["domain"] previous_uuid = old_domain.getTag("uuid").getData() previous_name = old_domain.getTag("name").getData() new_uuid = domain.getTag("uuid").getData() @@ -374,21 +427,33 @@ if domain.getTag('description'): domain.delChild("description") domain.addChild(node=old_domain.getTag("description")) - vm_item.getTag("virtualmachine").delChild("domain") - vm_item.getTag("virtualmachine").addChild(node=domain) + self.update_vm_domain_in_db(uuid, domain) + self.entity.push_change("vmparking", "updated") - def publish_success(resp): - if resp.getType() == "result": - self.inhibit_next_general_push = True - self.entity.push_change("vmparking", "updated") - self.pubsub_vmparking.remove_item(ticket) - self.entity.log.info("VMPARKING: virtual machine %s as been updated" % new_uuid) - else: - self.inhibit_next_general_push = True - self.entity.push_change("vmparking", "cannot-update", content_node=resp) - self.entity.log.error("VMPARKING: unable to update item for virtual machine %s: %s" % (new_uuid, resp)) - self.pubsub_vmparking.add_item(vm_item.getTag("virtualmachine"), callback=publish_success) + def create_parked(self, vm_informations): + """ + Creates a VM directly into the parking. + @type vm_informations: list + @param vm_informations: list containing VM to park [{"uuid": x, domain: y, parker: x, creation_date: d, status: s}] + """ + for vm_info in vm_informations: + if self.is_vm_parked(vm_info["uuid"]): + raise Exception("VM with UUID %s is already parked" % vm_info["uuid"]) + + vm = self.entity.get_vm_by_uuid(vm_info["uuid"]) + if vm: + raise Exception("There is already a VM with UUID %s" % vm_info["uuid"]) + + if vm_info["domain"].getTag("description"): + raise Exception("You cannot park a VM XML with a tag. Please remove it") + + password = ''.join([random.choice(string.letters + string.digits) for i in range(32)]) + vm_info["domain"].addChild("description").setData("%s@%s::::%s" % (vm_info["uuid"], self.entity.jid.getDomain(), password)) + vm_info["domain"] = str(vm_info["domain"]).replace('xmlns=\"archipel:hypervisor:vmparking\"', '') + + self.register_vms_into_db(vm_informations) + self.entity.push_change("vmparking", "parked") ### XMPP Management for hypervisors @@ -399,6 +464,7 @@ It understands IQ of type: - list - park + - create_parked - unpark - destroy - updatexml @@ -420,6 +486,8 @@ reply = self.iq_delete(iq) if action == "updatexml": reply = self.iq_updatexml(iq) + if action == "create_parked": + reply = self.iq_create_parked(iq) if reply: conn.send(reply) raise xmpp.protocol.NodeProcessed @@ -480,15 +548,17 @@ """ try: items = iq.getTag("query").getTag("archipel").getTags("item") + vms_info = [] for item in items: vm_uuid = item.getAttr("uuid") if not vm_uuid: self.entity.log.error("VMPARKING: Unable to park vm: missing 'uuid' element.") raise Exception("You must must set the UUID of the vms you want to park") - force_destroy = False - if item.getAttr("force") and item.getAttr("force").lower() in ("yes", "y", "true", "1"): - force_destroy = True - self.park(vm_uuid, iq.getFrom(), force=force_destroy) + + vms_info.append({"uuid": vm_uuid, "status": ARCHIPEL_PARKING_STATUS_PARKED, "parker": str(iq.getFrom())}) + + self.park(vms_info) + reply = iq.buildReply("result") except Exception as ex: reply = build_error_iq(self, ex, iq, ARCHIPEL_ERROR_CODE_VMPARK_PARK) @@ -507,8 +577,12 @@ if len(tokens) < 2: return "I'm sorry, you use a wrong format. You can type 'help' to get help." uuids = tokens[1].split(",") + vms_info = [] for vmuuid in uuids: - self.park(vmuuid, msg.getFrom()) + vms_info.append({"uuid": vmuuid, "status": ARCHIPEL_PARKING_STATUS_PARKED, "parker": str(msg.getFrom())}) + + self.park(vms_info) + if len(uuids) == 1: return "Virtual machine is parking." else: @@ -527,12 +601,16 @@ try: reply = iq.buildReply("result") items = iq.getTag("query").getTag("archipel").getTags("item") + vms_info = [] for item in items: identifier = item.getAttr("identifier") autostart = False if item.getAttr("start") and item.getAttr("start").lower() in ("yes", "y", "true", "1"): autostart = True - self.unpark(identifier, start=autostart) + vms_info.append({"uuid": identifier, "status": ARCHIPEL_PARKING_STATUS_NOT_PARKED, "start": autostart, "parker": str(iq.getFrom())}) + + self.unpark(vms_info) + except Exception as ex: reply = build_error_iq(self, ex, iq, ARCHIPEL_ERROR_CODE_VMPARK_UNPARK) return reply @@ -550,8 +628,12 @@ if len(tokens) < 2: return "I'm sorry, you use a wrong format. You can type 'help' to get help." itemids = tokens[1].split(",") + vms_info = [] for itemid in itemids: - self.unpark(itemid) + vms_info.append({"uuid": itemid, "start": False, "status": ARCHIPEL_PARKING_STATUS_NOT_PARKED, "parker": str(msg.getFrom())}) + + self.unpark(vms_info) + if len(itemids) == 1: return "Virtual machine is unparking." else: @@ -570,9 +652,13 @@ try: reply = iq.buildReply("result") items = iq.getTag("query").getTag("archipel").getTags("item") + vm_uuids = [] + for item in items: - identifier = item.getAttr("identifier") - self.delete(identifier) + vm_uuids.append({"uuid": item.getAttr("identifier")}) + + self.delete(vm_uuids) + except Exception as ex: reply = build_error_iq(self, ex, iq, ARCHIPEL_ERROR_CODE_VMPARK_DELETE) return reply @@ -594,6 +680,32 @@ reply = build_error_iq(self, ex, iq, ARCHIPEL_ERROR_CODE_VMPARK_UPDATEXML) return reply + def iq_create_parked(self, iq): + """ + Create a VM in directly into the parking + @type iq: xmpp.Protocol.Iq + @param iq: the received IQ + @rtype: xmpp.Protocol.Iq + @return: a ready to send IQ containing the result of the action + """ + try: + items = iq.getTag("query").getTag("archipel").getTags("item") + vms_info = [] + for item in items: + vm_uuid = item.getAttr("uuid") + if not vm_uuid: + self.entity.log.error("VMPARKING: Unable to park vm: missing 'uuid' element.") + raise Exception("You must must set the UUID of the vms you want to park") + vm_domain = item.getTag("domain") + vms_info.append({"uuid": vm_uuid, "domain": vm_domain, "parker": str(iq.getFrom()), "creation_date": datetime.datetime.now(), "status": ARCHIPEL_PARKING_STATUS_PARKED}) + + self.create_parked(vms_info) + + reply = iq.buildReply("result") + except Exception as ex: + reply = build_error_iq(self, ex, iq, ARCHIPEL_ERROR_CODE_VMPARK_CREATE_PARKED) + return reply + ## XMPP Management for hypervisors @@ -626,7 +738,8 @@ """ try: reply = iq.buildReply("result") - self.entity.hypervisor.get_plugin("vmparking").park(self.entity.uuid, iq.getFrom()) + vms_info = [{"uuid": self.entity.uuid, "status": ARCHIPEL_PARKING_STATUS_PARKED, "parker": str(iq.getFrom())}] + self.entity.hypervisor.get_plugin("vmparking").park(vms_info) except Exception as ex: reply = build_error_iq(self, ex, iq, ARCHIPEL_ERROR_CODE_VMPARK_PARK) return reply @@ -643,7 +756,8 @@ tokens = msg.getBody().split() if not len(tokens) == 1: return "I'm sorry, you use a wrong format. You can type 'help' to get help." - self.entity.hypervisor.get_plugin("vmparking").park(self.entity.uuid, msg.getFrom()) + vms_info = [{"uuid": self.entity.uuid, "status": ARCHIPEL_PARKING_STATUS_PARKED, "parker": str(msg.getFrom())}] + self.entity.hypervisor.get_plugin("vmparking").park(vms_info) return "I'm parking." except Exception as ex: return build_error_message(self, ex, msg) diff -Nru archipel-agent-vmparking-0.5.0/debian/changelog archipel-agent-vmparking-0.6.0/debian/changelog --- archipel-agent-vmparking-0.5.0/debian/changelog 2012-11-02 20:38:59.000000000 +0000 +++ archipel-agent-vmparking-0.6.0/debian/changelog 2013-05-05 08:56:32.000000000 +0000 @@ -1,3 +1,11 @@ +archipel-agent-vmparking (0.6.0-1) unstable; urgency=low + + * New upstream beta release. + * New maintainer (closes: #704318). + * Add watch file. + + -- Laszlo Boszormenyi (GCS) Tue, 30 Apr 2013 07:49:15 +0000 + archipel-agent-vmparking (0.5.0-1) unstable; urgency=low * Initial release. diff -Nru archipel-agent-vmparking-0.5.0/debian/clean archipel-agent-vmparking-0.6.0/debian/clean --- archipel-agent-vmparking-0.5.0/debian/clean 1970-01-01 00:00:00.000000000 +0000 +++ archipel-agent-vmparking-0.6.0/debian/clean 2013-05-05 09:04:28.000000000 +0000 @@ -0,0 +1 @@ +archipel_agent_vmparking.egg-info/* diff -Nru archipel-agent-vmparking-0.5.0/debian/control archipel-agent-vmparking-0.6.0/debian/control --- archipel-agent-vmparking-0.5.0/debian/control 2012-11-02 20:38:44.000000000 +0000 +++ archipel-agent-vmparking-0.6.0/debian/control 2013-04-30 07:50:08.000000000 +0000 @@ -1,7 +1,7 @@ Source: archipel-agent-vmparking Section: python Priority: optional -Maintainer: Daniel Baumann +Maintainer: Laszlo Boszormenyi (GCS) Build-Depends: debhelper (>= 9), python, python-setuptools Standards-Version: 3.9.4 Homepage: http://archipelproject.org/ diff -Nru archipel-agent-vmparking-0.5.0/debian/copyright archipel-agent-vmparking-0.6.0/debian/copyright --- archipel-agent-vmparking-0.5.0/debian/copyright 2012-11-02 20:38:46.000000000 +0000 +++ archipel-agent-vmparking-0.6.0/debian/copyright 2013-05-05 08:57:37.000000000 +0000 @@ -9,7 +9,8 @@ License: AGPL-3+ Files: debian/* -Copyright: 2012 Daniel Baumann +Copyright: 2013 Laszlo Boszormenyi (GCS) , + 2012 Daniel Baumann License: AGPL-3+ License: AGPL-3+ diff -Nru archipel-agent-vmparking-0.5.0/debian/watch archipel-agent-vmparking-0.6.0/debian/watch --- archipel-agent-vmparking-0.5.0/debian/watch 1970-01-01 00:00:00.000000000 +0000 +++ archipel-agent-vmparking-0.6.0/debian/watch 2013-05-05 08:56:24.000000000 +0000 @@ -0,0 +1,3 @@ +version=3 +opts="uversionmangle=s/(\d[\.\d]*)/0.$1.0/" \ +https://github.com/ArchipelProject/Archipel/tags .*/@beta(\d[\.\d]*)\.(?:tgz|tbz2|txz|tar\.(?:gz|bz2|xz)) diff -Nru archipel-agent-vmparking-0.5.0/install/bin/archipel-vmparkingnode archipel-agent-vmparking-0.6.0/install/bin/archipel-vmparkingnode --- archipel-agent-vmparking-0.5.0/install/bin/archipel-vmparkingnode 2012-11-02 13:54:29.000000000 +0000 +++ archipel-agent-vmparking-0.6.0/install/bin/archipel-vmparkingnode 1970-01-01 00:00:00.000000000 +0000 @@ -1,213 +0,0 @@ -#!/usr/bin/python -W ignore::DeprecationWarning -# -# archipel-vmrequestnode -# -# Copyright (C) 2010 Antoine Mercadal -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see .archipel-vmrequestnode - - -import os -import sys -from optparse import OptionParser -import xmpp -import archipelcore.pubsub -from archipelcore.scriptutils import * - - -NODENAME_VMPARKING = "/archipel/vmparking" - -def create(xmppclient, pubsubserver): - """ - Create the pubsub node - """ - config = { - archipelcore.pubsub.XMPP_PUBSUB_VAR_ACCESS_MODEL: archipelcore.pubsub.XMPP_PUBSUB_VAR_ACCESS_MODEL_AUTHORIZE, - archipelcore.pubsub.XMPP_PUBSUB_VAR_PUBLISH_MODEL: archipelcore.pubsub.XMPP_PUBSUB_VAR_ACCESS_MODEL_AUTHORIZE, - archipelcore.pubsub.XMPP_PUBSUB_VAR_DELIVER_NOTIFICATION: 1, - archipelcore.pubsub.XMPP_PUBSUB_VAR_MAX_ITEMS: 1000, - archipelcore.pubsub.XMPP_PUBSUB_VAR_PERSIST_ITEMS: 1, - archipelcore.pubsub.XMPP_PUBSUB_VAR_NOTIFY_RECTRACT: 1, - archipelcore.pubsub.XMPP_PUBSUB_VAR_DELIVER_PAYLOADS: 1, - archipelcore.pubsub.XMPP_PUBSUB_VAR_SEND_LAST_PUBLISHED_ITEM: archipelcore.pubsub.XMPP_PUBSUB_VAR_SEND_LAST_PUBLISHED_ITEM_NEVER, - archipelcore.pubsub.XMPP_PUBSUB_VAR_ITEM_REPLY: archipelcore.pubsub.XMPP_PUBSUB_VAR_ITEM_REPLY_PUBLISHER - } - create_pubsub(xmppclient, pubsubserver, NODENAME_VMPARKING, config) - configure_pubsub(xmppclient, pubsubserver, NODENAME_VMPARKING, archipelcore.pubsub.XMPP_PUBSUB_VAR_MAX_ITEMS, 1000) - - -def delete(xmppclient, pubsubserver): - """ - Delete the pubsub node - """ - delete_pubsub(xmppclient, pubsubserver, NODENAME_VMPARKING) - - -def list_affiliations(xmppclient, pubsubserver): - """ - List the affiliatons - """ - pubSubNode = get_pubsub(xmppclient, pubsubserver, NODENAME_VMPARKING) - if pubSubNode.fetch_affiliations(wait=True): - if len(pubSubNode.affiliations) == 0: print "Parking authorized accounts" - for jid, aff in pubSubNode.affiliations.items(): - print " - %s (%s)" % (jid, aff) - else: - error("Unable to add set affiliation to node %s" % NODENAME_VMPARKING) - - -def set_affiliation(xmppclient, account, affiliation, pubsubserver): - """ - Set the affiliation of an account - @type jid: xmpp.JID - @param jid: what - """ - check_valid_jid(account, bare=True) - pubSubNode = get_pubsub(xmppclient, pubsubserver, NODENAME_VMPARKING) - if pubSubNode.set_affiliation(account, affiliation, wait=True): - success("JID %s affiliation is now %s" % (account, affiliation)) - else: - error("Unable to set affiliation %s to account %s" % (affiliation, account)) - -def content(xmppclient, pubsubserver, show_domain=False): - """ - List the content of the parking - """ - pubSubNode = get_pubsub(xmppclient, pubsubserver, NODENAME_VMPARKING) - for item in pubSubNode.get_items(): - print "ID : %s" % item.getAttr("id") - print "Name : %s" % item.getTag("virtualmachine").getTag("domain").getTag("name").getData() - print "Parker : %s" % item.getTag("virtualmachine").getAttr("parker") - print "Date : %s" % item.getTag("virtualmachine").getAttr("date") - if show_domain: - print "XML : " - print "%s" % str(item.getTag("virtualmachine").getTag("domain")).replace('xmlns="http://www.gajim.org/xmlns/undeclared" ', "") - print "" - -def delete_item(xmppclient, pubsubserver, pid): - """ - delete a parked VM - """ - if retract_item(xmppclient, pubsubserver, NODENAME_VMPARKING, pid, wait=True): - success("VM Parking with ID %s has been removed" % pid) - else: - error("Unable to remove VM with parking ticket %s" % pid) - - -def park(xmppclient, pubsubserver, name, uuid, xml_string): - """ - Directly park a virtual machine - """ - import random - import string - import datetime - xml = xmpp.simplexml.NodeBuilder(data=xml_string).getDom() - if xml.getTag("uuid"): - xml.delChild("uuid") - xml.addChild("uuid").setData(uuid) - if xml.getTag("name"): - xml.delChild("name") - xml.addChild("name").setData(name) - if xml.getTag("description"): - xml.delChild("description") - password = ''.join([random.choice(string.letters + string.digits) for i in range(32)]) - xml.addChild("description").setData("%s@%s::::%s" % (uuid, xmppclient.Server, password)) - vmparkednode = xmpp.Node(tag="virtualmachine", attrs={"parker": "from@script", "date": datetime.datetime.now(), "origin": "from@script"}) - vmparkednode.addChild(node=xml) - publish_item(xmppclient, pubsubserver, NODENAME_VMPARKING, vmparkednode, wait=True) - - -if __name__ == "__main__": - parser = OptionParser() - parser.add_option("-j", "--jid", - dest="jid", - help="set the JID to use", - metavar="user@domain") - parser.add_option("-p", "--password", - dest="password", - help="set the password associated to the JID", - metavar="123456") - parser.add_option("-P", "--pubsubserver", - dest="pubsubserver", - help="set the pubsubserver to use. if not given it will be pubsub.[jid.getDomain()]", - metavar="pubsub.domain", - default=None) - parser.add_option("-c", "--create", - action="store_true", - dest="create", - help="create the node (default action)") - parser.add_option("-d", "--delete", - action="store_true", - dest="delete", - help="delete the node") - parser.add_option("-a", "--authorize", - dest="auth", - help="authorize an hypervisor to handle the parking", - metavar="user@domain") - parser.add_option("-u", "--unauthorize", - dest="unauth", - help="unauthorize an hypervisor to handle the parking", - metavar="user@domain") - parser.add_option("--park", - action="store_true", - help="directly park a XML description") - parser.add_option("--uuid", - dest="park_uuid", - help="UUID of a the VM to park", - metavar="uuid") - parser.add_option("--xml", - dest="park_xml_string", - help="the libvirt description", - metavar="libvirt desc") - parser.add_option("--name", - dest="park_name", - help="the name of the virtual machine", - metavar="a name") - parser.add_option("-l", "--list", - action="store_true", - dest="list", - help="List authorized accounts") - parser.add_option("-L", "--List", - action="store_true", - dest="listContent", - help="List parked information") - parser.add_option("-r", "--remove", - dest="delete_item", - help="Delete a parked VM") - parser.add_option("-v", "--verbose", - action="store_true", - dest="verbose", - help="verbose mode") - - options, args = parser.parse_args() - - xmppclient = initialize(options) - - if options.list: - list_affiliations(xmppclient, options.pubsubserver) - elif options.auth: - set_affiliation(xmppclient, xmpp.JID(options.auth), archipelcore.pubsub.XMPP_PUBSUB_AFFILIATION_OWNER, options.pubsubserver) - elif options.unauth: - set_affiliation(xmppclient, xmpp.JID(options.unauth), archipelcore.pubsub.XMPP_PUBSUB_AFFILIATION_NONE, options.pubsubserver) - elif options.create: - create(xmppclient, options.pubsubserver) - elif options.delete: - delete(xmppclient, options.pubsubserver) - elif options.listContent: - content(xmppclient, options.pubsubserver, options.verbose) - elif options.delete_item: - delete_item(xmppclient, options.pubsubserver, options.delete_item) - elif options.park: - if not options.park_uuid or not options.park_xml_string or not options.park_name: - error("You must set the UUID and the XML string if you want to park directly a virtual machine") - park(xmppclient, options.pubsubserver, options.park_name, options.park_uuid, options.park_xml_string) \ No newline at end of file diff -Nru archipel-agent-vmparking-0.5.0/setup.py archipel-agent-vmparking-0.6.0/setup.py --- archipel-agent-vmparking-0.5.0/setup.py 2012-11-02 13:54:29.000000000 +0000 +++ archipel-agent-vmparking-0.6.0/setup.py 2013-03-21 00:58:52.000000000 +0000 @@ -17,7 +17,7 @@ from setuptools import setup, find_packages -VERSION = '0.5.0' +VERSION = '0.6.0' AUTHOR = 'Antoine Mercadal' MAIL = 'antoine.mercadal@archipelproject.org' @@ -74,10 +74,7 @@ include_package_data=True, zip_safe=False, install_requires=[ - "archipel-core>=0.5.0beta" + "archipel-core>=0.6.0beta" ], - entry_points=ENTRY_POINTS, - scripts = [ - 'install/bin/archipel-vmparkingnode', - ] - ) \ No newline at end of file + entry_points=ENTRY_POINTS + )