Darryl L. Pierce
2009-Dec-08 21:13 UTC
[Ovirt-devel] Rebased patches, fixed a rebasing problem...
The previous patch set had an error and one development branch's changes showed up twice, and two different change sets. So it was missing the bulk of the configuration work. This patch set fixes that.
Darryl L. Pierce
2009-Dec-08 21:13 UTC
[Ovirt-devel] [PATCH 1/2] Users can now work with remote libvirt hosts.
The user can: * select a remote machine * add a remote machine * remove a remote machine Signed-off-by: Darryl L. Pierce <dpierce at redhat.com> --- ...rs-can-now-work-with-remote-libvirt-hosts.patch | 628 ++++++++++++++++++++ ...rs-to-migrate-virtual-machines-between-ho.patch | 253 ++++++++ Makefile.am | 5 + nodeadmin/addhost.py | 129 ++++ nodeadmin/changehost.py | 58 ++ nodeadmin/configscreen.py | 36 ++- nodeadmin/definenet.py | 1 + nodeadmin/hostconnect.py | 29 + nodeadmin/hostmenu.py | 46 ++ nodeadmin/libvirtworker.py | 53 ++- nodeadmin/mainmenu.py | 24 +- nodeadmin/removehost.py | 66 ++ ovirt-node.spec.in | 5 + 13 files changed, 1320 insertions(+), 13 deletions(-) create mode 100644 0001-Users-can-now-work-with-remote-libvirt-hosts.patch create mode 100644 0002-Enables-users-to-migrate-virtual-machines-between-ho.patch create mode 100644 nodeadmin/addhost.py create mode 100644 nodeadmin/changehost.py create mode 100644 nodeadmin/hostconnect.py create mode 100644 nodeadmin/hostmenu.py create mode 100644 nodeadmin/removehost.py diff --git a/0001-Users-can-now-work-with-remote-libvirt-hosts.patch b/0001-Users-can-now-work-with-remote-libvirt-hosts.patch new file mode 100644 index 0000000..a6c2342 --- /dev/null +++ b/0001-Users-can-now-work-with-remote-libvirt-hosts.patch @@ -0,0 +1,628 @@ +From f4bd14953ef3ff1335b6980563ebbf64cb97153a Mon Sep 17 00:00:00 2001 +From: Darryl L. Pierce <dpierce at redhat.com> +Date: Wed, 28 Oct 2009 16:29:53 -0400 +Subject: [PATCH 1/2] Users can now work with remote libvirt hosts. + +The user can: + * select a remote machine + * add a remote machine + * remove a remote machine +--- + Makefile.am | 5 ++ + nodeadmin/addhost.py | 129 ++++++++++++++++++++++++++++++++++++++++++++ + nodeadmin/changehost.py | 58 ++++++++++++++++++++ + nodeadmin/configscreen.py | 36 ++++++++++++- + nodeadmin/definenet.py | 1 + + nodeadmin/hostconnect.py | 29 ++++++++++ + nodeadmin/hostmenu.py | 46 ++++++++++++++++ + nodeadmin/libvirtworker.py | 53 +++++++++++++++++- + nodeadmin/mainmenu.py | 14 +++-- + nodeadmin/removehost.py | 66 ++++++++++++++++++++++ + ovirt-node.spec.in | 5 ++ + 11 files changed, 434 insertions(+), 8 deletions(-) + create mode 100644 nodeadmin/addhost.py + create mode 100644 nodeadmin/changehost.py + create mode 100644 nodeadmin/hostconnect.py + create mode 100644 nodeadmin/hostmenu.py + create mode 100644 nodeadmin/removehost.py + +diff --git a/Makefile.am b/Makefile.am +index b3929de..1671405 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -28,11 +28,15 @@ EXTRA_DIST = \ + images/syslinux-vesa-splash.jpg \ + nodeadmin/__init__.py \ + nodeadmin/adddomain.py \ ++ nodeadmin/addhost.py \ ++ nodeadmin/changehost.py \ + nodeadmin/configscreen.py \ + nodeadmin/createnetwork.py \ + nodeadmin/createuser.py \ + nodeadmin/destroynetwork.py \ + nodeadmin/halworker.py \ ++ nodeadmin/hostconnect.py \ ++ nodeadmin/hostmenu.py \ + nodeadmin/libvirtworker.py \ + nodeadmin/userworker.py \ + nodeadmin/mainmenu.py \ +@@ -40,6 +44,7 @@ EXTRA_DIST = \ + nodeadmin/netmenu.py \ + nodeadmin/nodemenu.py \ + nodeadmin/removedomain.py \ ++ nodeadmin/removehost.py \ + nodeadmin/undefinenetwork.py \ + nodeadmin/startdomain.py \ + nodeadmin/stopdomain.py \ +diff --git a/nodeadmin/addhost.py b/nodeadmin/addhost.py +new file mode 100644 +index 0000000..ef35b7d +--- /dev/null ++++ b/nodeadmin/addhost.py +@@ -0,0 +1,129 @@ ++# addhost.py - Copyright (C) 2009 Red Hat, Inc. ++# Written by Darryl L. Pierce <dpierce at redhat.com> ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; version 2 of the License. ++# ++# 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 General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ++# MA 02110-1301, USA. A copy of the GNU General Public License is ++# also available at http://www.gnu.org/copyleft/gpl.html. ++ ++from snack import * ++ ++from configscreen import * ++ ++DETAILS_PAGE = 1 ++CONFIRM_PAGE = 2 ++ ++HYPERVISOR_XEN = "xen" ++HYPERVISOR_KVM = "kvm" ++ ++HYPERVISORS = {HYPERVISOR_XEN : "Xen", ++ HYPERVISOR_KVM : "QEMU/KVM"} ++ ++CONNECTION_LOCAL = "local" ++CONNECTION_KERBEROS = "kerberos" ++CONNECTION_SSL = "ssl" ++CONNECTION_SSH = "ssh" ++ ++CONNECTIONS = {CONNECTION_LOCAL : "Local", ++ CONNECTION_KERBEROS : "Remote Password or Kerberos", ++ CONNECTION_SSL : "Remote SSL/TLS with x509 certificate", ++ CONNECTION_SSH : "Remote tunnel over SSH"} ++ ++class AddHostConfigScreen(ConfigScreen): ++ def __init__(self): ++ ConfigScreen.__init__(self, "Add A Remote Host") ++ self.__configured = False ++ ++ def get_elements_for_page(self, screen, page): ++ if page is DETAILS_PAGE: return self.get_details_page(screen) ++ elif page is CONFIRM_PAGE: return self.get_confirm_page(screen) ++ ++ def page_has_next(self, page): ++ return page < CONFIRM_PAGE ++ ++ def page_has_back(self, page): ++ return page > DETAILS_PAGE ++ ++ def page_has_finish(self, page): ++ return page is CONFIRM_PAGE ++ ++ def validate_input(self, page, errors): ++ if page is DETAILS_PAGE: ++ if len(self.__hostname.value()) > 0: ++ return True ++ else: ++ errors.append("You must enter a remote hostname.") ++ elif page is CONFIRM_PAGE: return True ++ return False ++ ++ def process_input(self, page): ++ if page is CONFIRM_PAGE: ++ hv = self.__hypervisor.getSelection() ++ conn = self.__connection.getSelection() ++ hostname = self.__hostname.value() ++ ++ if hv is HYPERVISOR_XEN: ++ if conn is CONNECTION_LOCAL: url = "xen:///" ++ elif conn is CONNECTION_KERBEROS: url = "xen+tcp:///" + hostname + "/" ++ elif conn is CONNECTION_SSL: url = "xen+tls:///" + hostname + "/" ++ elif conn is CONNECTION_SSH: url = "xen+ssh:///" + hostname + "/" ++ elif hv is HYPERVISOR_KVM: ++ if conn is CONNECTION_LOCAL: url = "qemu:///system" ++ elif conn is CONNECTION_KERBEROS: url = "qemu+tcp://" + hostname + "/system" ++ elif conn is CONNECTION_SSL: url = "qemu+tls://" + hostname + "/system" ++ elif conn is CONNECTION_SSH: url = "qemu+ssh://" + hostname + "/system" ++ ++ self.get_virt_manager_config().add_connection(url) ++ self.set_finished() ++ ++ def get_details_page(self, screen): ++ if not self.__configured: ++ self.__hypervisor = RadioBar(screen, ((HYPERVISORS[HYPERVISOR_XEN], HYPERVISOR_XEN, True), ++ (HYPERVISORS[HYPERVISOR_KVM], HYPERVISOR_KVM, False))) ++ self.__connection = RadioBar(screen, ((CONNECTIONS[CONNECTION_LOCAL], CONNECTION_LOCAL, True), ++ (CONNECTIONS[CONNECTION_KERBEROS], CONNECTION_KERBEROS, False), ++ (CONNECTIONS[CONNECTION_SSL], CONNECTION_SSL, False), ++ (CONNECTIONS[CONNECTION_SSH], CONNECTION_SSH, False))) ++ self.__hostname = Entry(50, "") ++ self.__autoconnect = Checkbox("Autoconnect on Startup") ++ self.__configured = True ++ grid = Grid(2, 4) ++ grid.setField(Label("Hypervisor:"), 0, 0, anchorRight = 1, anchorTop = 1) ++ grid.setField(self.__hypervisor, 1, 0, anchorLeft = 1) ++ grid.setField(Label("Connection:"), 0, 1, anchorRight = 1, anchorTop = 1) ++ grid.setField(self.__connection, 1, 1, anchorLeft = 1) ++ grid.setField(Label("Hostname:"), 0, 2, anchorRight = 1) ++ grid.setField(self.__hostname, 1, 2, anchorLeft = 1) ++ grid.setField(Label(""), 0, 3, anchorRight = 1) ++ grid.setField(self.__autoconnect, 1, 3, anchorLeft = 1) ++ return [Label("Add Connection"), ++ grid] ++ ++ def get_confirm_page(self, screen): ++ grid = Grid(2, 4) ++ grid.setField(Label("Hypervisor:"), 0, 0, anchorRight = 1) ++ grid.setField(Label(HYPERVISORS[self.__hypervisor.getSelection()]), 1, 0, anchorLeft = 1) ++ grid.setField(Label("Connection:"), 0, 1, anchorRight = 1) ++ grid.setField(Label(CONNECTIONS[self.__connection.getSelection()]), 1, 1, anchorLeft = 1) ++ grid.setField(Label("Hostname:"), 0, 2, anchorRight = 1) ++ grid.setField(Label(self.__hostname.value()), 1, 2, anchorLeft = 1) ++ grid.setField(Label("Autoconnect on Startup:"), 0, 3, anchorRight = 1) ++ label = "Yes" ++ if not self.__autoconnect.value(): label = "No" ++ grid.setField(Label(label), 1, 3, anchorLeft = 1) ++ return [Label("Confirm Connection"), ++ grid] ++ ++def AddHost(): ++ screen = AddHostConfigScreen() ++ screen.start() +diff --git a/nodeadmin/changehost.py b/nodeadmin/changehost.py +new file mode 100644 +index 0000000..23e6854 +--- /dev/null ++++ b/nodeadmin/changehost.py +@@ -0,0 +1,58 @@ ++# changehost.py - Copyright (C) 2009 Red Hat, Inc. ++# Written by Darryl L. Pierce <dpierce at redhat.com> ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; version 2 of the License. ++# ++# 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 General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ++# MA 02110-1301, USA. A copy of the GNU General Public License is ++# also available at http://www.gnu.org/copyleft/gpl.html. ++ ++from snack import * ++ ++import logging ++import libvirtworker ++from configscreen import * ++ ++CONNECTION_LIST_PAGE = 1 ++CONNECTED_PAGE = 2 ++ ++class ChangeHostConfigScreen(HostListConfigScreen): ++ def __init__(self): ++ HostListConfigScreen.__init__(self, "Change Host") ++ ++ def get_elements_for_page(self, screen, page): ++ if page is CONNECTION_LIST_PAGE: return self.get_connection_list_page(screen) ++ elif page is CONNECTED_PAGE: return self.get_connected_page(screen) ++ ++ def process_input(self, page): ++ if page is CONNECTION_LIST_PAGE: ++ logging.info("Changing libvirt connection to %s" % self.get_selected_connection()) ++ libvirtworker.set_default_url(self.get_selected_connection()) ++ self.get_libvirt().open_connection(self.get_selected_connection()) ++ elif page is CONNECTED_PAGE: self.set_finished() ++ ++ def page_has_next(self, page): ++ if page is CONNECTION_LIST_PAGE: return self.has_selectable_connections() ++ return False ++ ++ def page_has_back(self, page): ++ return page > CONNECTION_LIST_PAGE ++ ++ def page_has_finish(self, page): ++ return page is CONNECTED_PAGE ++ ++ def get_connected_page(self, screen): ++ return [Label("Connected to %s" % self.get_selected_connection())] ++ ++def ChangeHost(): ++ screen = ChangeHostConfigScreen() ++ screen.start() +diff --git a/nodeadmin/configscreen.py b/nodeadmin/configscreen.py +index f214aea..98e0338 100644 +--- a/nodeadmin/configscreen.py ++++ b/nodeadmin/configscreen.py +@@ -18,7 +18,7 @@ + + from snack import * + from halworker import HALWorker +-from libvirtworker import LibvirtWorker ++from libvirtworker import * + import traceback + + BACK_BUTTON = "back" +@@ -35,6 +35,7 @@ class ConfigScreen: + self.__finished = False + self.__hal = HALWorker() + self.__libvirt = LibvirtWorker() ++ self.__vm_config = VirtManagerConfig() + + def get_hal(self): + return self.__hal +@@ -42,6 +43,9 @@ class ConfigScreen: + def get_libvirt(self): + return self.__libvirt + ++ def get_virt_manager_config(self): ++ return self.__vm_config ++ + def set_finished(self): + self.__finished = True + +@@ -179,3 +183,33 @@ class NetworkListConfigScreen(ConfigScreen): + + def has_selectable_networks(self): + return self.__has_networks ++ ++class HostListConfigScreen(ConfigScreen): ++ '''Provides a base class for working with lists of libvirt hosts.''' ++ ++ def __init__(self, title): ++ ConfigScreen.__init__(self, title) ++ ++ def get_connection_list_page(self, screen): ++ connections = self.get_virt_manager_config().get_connection_list() ++ result = None ++ ++ if len(connections) > 0: ++ self.__has_connections = True ++ self.__connection_list = Listbox(0) ++ for connection in connections: ++ self.__connection_list.append(connection, connection) ++ result = self.__connection_list ++ else: ++ self.__has_connections = False ++ result = Label("There are no defined connections.") ++ grid = Grid(1, 1) ++ grid.setField(result, 0, 0) ++ return [Label("Host List"), ++ grid] ++ ++ def get_selected_connection(self): ++ return self.__connection_list.current() ++ ++ def has_selectable_connections(self): ++ return self.__has_connections +diff --git a/nodeadmin/definenet.py b/nodeadmin/definenet.py +index 4aa37d5..6dff18f 100644 +--- a/nodeadmin/definenet.py ++++ b/nodeadmin/definenet.py +@@ -20,6 +20,7 @@ from snack import * + from IPy import IP + import traceback + import logging ++import re + + from configscreen import ConfigScreen + from networkconfig import NetworkConfig +diff --git a/nodeadmin/hostconnect.py b/nodeadmin/hostconnect.py +new file mode 100644 +index 0000000..a1be569 +--- /dev/null ++++ b/nodeadmin/hostconnect.py +@@ -0,0 +1,29 @@ ++# hostconnect.py - Copyright (C) 2009 Red Hat, Inc. ++# Written by Darryl L. Pierce <dpierce at redhat.com> ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; version 2 of the License. ++# ++# 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 General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ++# MA 02110-1301, USA. A copy of the GNU General Public License is ++# also available at http://www.gnu.org/copyleft/gpl.html. ++ ++from snack import * ++ ++from configscreen import * ++ ++class HostConnectConfigScreen(ConfigScreen): ++ def __init__(self): ++ ConfigScree ++ ++def HostConnect(): ++ screen = HostConnectConfigScreen() ++ screen.start() +diff --git a/nodeadmin/hostmenu.py b/nodeadmin/hostmenu.py +new file mode 100644 +index 0000000..4054d6b +--- /dev/null ++++ b/nodeadmin/hostmenu.py +@@ -0,0 +1,46 @@ ++# hostmenu.py - Copyright (C) 2009 Red Hat, Inc. ++# Written by Darryl L. Pierce <dpierce at redhat.com> ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; version 2 of the License. ++# ++# 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 General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ++# MA 02110-1301, USA. A copy of the GNU General Public License is ++# also available at http://www.gnu.org/copyleft/gpl.html. ++ ++from snack import * ++ ++from menuscreen import MenuScreen ++from changehost import ChangeHost ++from addhost import AddHost ++from removehost import RemoveHost ++ ++SELECT_HOST = 1 ++ADD_HOST = 2 ++REMOVE_HOST = 3 ++ ++class HostMenuScreen(MenuScreen): ++ def __init__(self): ++ MenuScreen.__init__(self, "Host Menu Screen") ++ ++ def get_menu_items(self): ++ return (("Select A Host", SELECT_HOST), ++ ("Add A Host", ADD_HOST), ++ ("Remove A Host", REMOVE_HOST)) ++ ++ def handle_selection(self, item): ++ if item is SELECT_HOST: ChangeHost() ++ elif item is ADD_HOST: AddHost() ++ elif item is REMOVE_HOST: RemoveHost() ++ ++def HostMenu(): ++ screen = HostMenuScreen() ++ screen.start() +diff --git a/nodeadmin/libvirtworker.py b/nodeadmin/libvirtworker.py +index ba07605..2998486 100644 +--- a/nodeadmin/libvirtworker.py ++++ b/nodeadmin/libvirtworker.py +@@ -21,20 +21,69 @@ import libvirt + import os + import virtinst + import utils ++import logging + + from domainconfig import DomainConfig + + DEFAULT_POOL_TARGET_PATH="/var/lib/libvirt/images" ++DEFAULT_URL="qemu:///system" ++ ++default_url = DEFAULT_URL ++ ++def set_default_url(url): ++ logging.info("Changing DEFAULT_URL to %s" % url) ++ global default_url ++ ++ default_url = url ++ ++def get_default_url(): ++ logging.info("Returning default URL of %s" % default_url) ++ return default_url ++ ++class VirtManagerConfig: ++ def __init__(self, filename = "/etc/remote-libvirt.conf"): ++ self.__filename = filename ++ ++ def get_connection_list(self): ++ result = [] ++ if os.path.exists(self.__filename): ++ input = file(self.__filename, "r") ++ for entry in input: result.append(entry[0:-1]) ++ return result ++ ++ def add_connection(self, connection): ++ connections = self.get_connection_list() ++ if connections.count(connection) is 0: ++ connections.append(connection) ++ self._save_connections(connections) ++ ++ def remove_connection(self, connection): ++ connections = self.get_connection_list() ++ if connections.count(connection) > 0: ++ connections.remove(connection) ++ self._save_connections(connections) ++ ++ def _save_connections(self, connections): ++ output = file(self.__filename, "w") ++ for entry in connections: ++ print >> output, entry ++ output.close + + class LibvirtWorker: + '''Provides utilities for interfacing with libvirt.''' +- def __init__(self, url = "qemu:///system"): +- self.__conn = libvirt.open(url) ++ def __init__(self, url = None): ++ if url is None: url = get_default_url() ++ logging.info("Connecting to libvirt: %s" % url) ++ self.open_connection(url) + self.__capabilities = virtinst.CapabilitiesParser.parse(self.__conn.getCapabilities()) + self.__net = virtinst.VirtualNetworkInterface(conn = self.__conn) + self.__net.setup(self.__conn) + (self.__new_guest, self.__new_domain) = virtinst.CapabilitiesParser.guest_lookup(conn = self.__conn) + ++ def open_connection(self, url): ++ '''Lets the user change the url for the connection.''' ++ self.__conn = libvirt.open(url) ++ + def list_domains(self, defined = True, started = True): + '''Lists all domains.''' + result = [] +diff --git a/nodeadmin/mainmenu.py b/nodeadmin/mainmenu.py +index 73501fa..944ffeb 100755 +--- a/nodeadmin/mainmenu.py ++++ b/nodeadmin/mainmenu.py +@@ -19,15 +19,17 @@ + from snack import * + import traceback + +-from menuscreen import MenuScreen +-from nodemenu import NodeMenu +-from netmenu import NetworkMenu ++from menuscreen import MenuScreen ++from nodemenu import NodeMenu ++from netmenu import NetworkMenu ++from hostmenu import HostMenu + + import utils + import logging + + NODE_MENU = 1 + NETWORK_MENU = 2 ++HOST_MENU = 3 + EXIT_CONSOLE = 99 + + class MainMenuScreen(MenuScreen): +@@ -35,12 +37,14 @@ class MainMenuScreen(MenuScreen): + MenuScreen.__init__(self, "Main Menu") + + def get_menu_items(self): +- return (("Node Administration", NODE_MENU), +- ("Network Administration", NETWORK_MENU)) ++ return (("Node Administration", NODE_MENU), ++ ("Network Administration", NETWORK_MENU), ++ ("Host Administration", HOST_MENU)) + + def handle_selection(self, page): + if page is NODE_MENU: NodeMenu() + elif page is NETWORK_MENU: NetworkMenu() ++ elif page is HOST_MENU: HostMenu() + + def MainMenu(): + screen = MainMenuScreen() +diff --git a/nodeadmin/removehost.py b/nodeadmin/removehost.py +new file mode 100644 +index 0000000..cf3c46c +--- /dev/null ++++ b/nodeadmin/removehost.py +@@ -0,0 +1,66 @@ ++# removehost.py - Copyright (C) 2009 Red Hat, Inc. ++# Written by Darryl L. Pierce <dpierce at redhat.com> ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; version 2 of the License. ++# ++# 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 General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ++# MA 02110-1301, USA. A copy of the GNU General Public License is ++# also available at http://www.gnu.org/copyleft/gpl.html. ++ ++from snack import * ++ ++from configscreen import * ++ ++SELECT_HOST_PAGE = 1 ++CONFIRM_REMOVE_PAGE = 2 ++ ++class RemoveHostConfigScreen(HostListConfigScreen): ++ def __init__(self): ++ HostListConfigScreen.__init__(self, "Remove Host Connection") ++ ++ def get_elements_for_page(self, screen, page): ++ if page is SELECT_HOST_PAGE: return self.get_connection_list_page(screen) ++ elif page is CONFIRM_REMOVE_PAGE: return self.get_confirm_remove_page(screen) ++ ++ def page_has_next(self, page): ++ return page is SELECT_HOST_PAGE and self.has_selectable_connections() ++ ++ def page_has_back(self, page): ++ return page is CONFIRM_REMOVE_PAGE ++ ++ def page_has_finish(self, page): ++ return page is CONFIRM_REMOVE_PAGE ++ ++ def validate_input(self, page, errors): ++ if page is SELECT_HOST_PAGE: return True ++ elif page is CONFIRM_REMOVE_PAGE: ++ if self.__confirm.value(): ++ return True ++ else: ++ errors.append("You must confirm removing the connection.") ++ return False ++ ++ def process_input(self, page): ++ if page is CONFIRM_REMOVE_PAGE: ++ self.get_virt_manager_config().remove_connection(self.get_selected_connection()) ++ self.set_finished() ++ ++ def get_confirm_remove_page(self, screen): ++ self.__confirm = Checkbox("Remove this connection: %s" % self.get_selected_connection(), 0) ++ grid = Grid(1, 1) ++ grid.setField(self.__confirm, 0, 0) ++ return [Label("Remove Host Connection"), ++ grid] ++ ++def RemoveHost(): ++ screen = RemoveHostConfigScreen() ++ screen.start() +diff --git a/ovirt-node.spec.in b/ovirt-node.spec.in +index 56e3aad..23ca2bf 100644 +--- a/ovirt-node.spec.in ++++ b/ovirt-node.spec.in +@@ -198,6 +198,11 @@ cd - + %{__install} -p -m0755 nodeadmin/destroynetwork.py %{buildroot}%{python_sitelib}/nodeadmin + %{__install} -p -m0755 nodeadmin/undefinenetwork.py %{buildroot}%{python_sitelib}/nodeadmin + ++%{__install} -p -m0755 nodeadmin/addhost.py %{buildroot}%{python_sitelib}/nodeadmin ++%{__install} -p -m0644 nodeadmin/changehost.py %{buildroot}%{python_sitelib}/nodeadmin ++%{__install} -p -m0755 nodeadmin/hostmenu.py %{buildroot}%{python_sitelib}/nodeadmin ++%{__install} -p -m0755 nodeadmin/removehost.py %{buildroot}%{python_sitelib}/nodeadmin ++ + %{__install} -p -m0755 nodeadmin/createuser.py %{buildroot}%{python_sitelib}/nodeadmin + + %{__install} -p -m0644 nodeadmin/halworker.py %{buildroot}%{python_sitelib}/nodeadmin +-- +1.6.5.2 + diff --git a/0002-Enables-users-to-migrate-virtual-machines-between-ho.patch b/0002-Enables-users-to-migrate-virtual-machines-between-ho.patch new file mode 100644 index 0000000..a56ce52 --- /dev/null +++ b/0002-Enables-users-to-migrate-virtual-machines-between-ho.patch @@ -0,0 +1,253 @@ +From c565f28b25bd6b77b6a61ce92c2c70248f08130d Mon Sep 17 00:00:00 2001 +From: Darryl L. Pierce <dpierce at redhat.com> +Date: Wed, 11 Nov 2009 10:51:23 -0500 +Subject: [PATCH 2/2] Enables users to migrate virtual machines between hosts. + +Users select a virtual machine on their current libvirt host. They then +select a target machine, which must have been previously configured as a +connection. They confirm the migration and then it runs. +--- + Makefile.am | 1 + + nodeadmin/addhost.py | 10 ++++- + nodeadmin/libvirtworker.py | 6 +++ + nodeadmin/migratedomain.py | 81 ++++++++++++++++++++++++++++++++++++++++++++ + nodeadmin/nodemenu.py | 28 +++++++++------ + nodeadmin/setup.py.in | 1 + + ovirt-node.spec.in | 2 + + 7 files changed, 115 insertions(+), 14 deletions(-) + create mode 100644 nodeadmin/migratedomain.py + +diff --git a/Makefile.am b/Makefile.am +index 1671405..f557ea2 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -41,6 +41,7 @@ EXTRA_DIST = \ + nodeadmin/userworker.py \ + nodeadmin/mainmenu.py \ + nodeadmin/menuscreen.py \ ++ nodeadmin/migratedomain.py \ + nodeadmin/netmenu.py \ + nodeadmin/nodemenu.py \ + nodeadmin/removedomain.py \ +diff --git a/nodeadmin/addhost.py b/nodeadmin/addhost.py +index ef35b7d..ebcb4ea 100644 +--- a/nodeadmin/addhost.py ++++ b/nodeadmin/addhost.py +@@ -59,7 +59,9 @@ class AddHostConfigScreen(ConfigScreen): + + def validate_input(self, page, errors): + if page is DETAILS_PAGE: +- if len(self.__hostname.value()) > 0: ++ if self.__connection.getSelection() is CONNECTION_LOCAL: ++ return True ++ elif len(self.__hostname.value()) > 0: + return True + else: + errors.append("You must enter a remote hostname.") +@@ -115,8 +117,12 @@ class AddHostConfigScreen(ConfigScreen): + grid.setField(Label(HYPERVISORS[self.__hypervisor.getSelection()]), 1, 0, anchorLeft = 1) + grid.setField(Label("Connection:"), 0, 1, anchorRight = 1) + grid.setField(Label(CONNECTIONS[self.__connection.getSelection()]), 1, 1, anchorLeft = 1) ++ if self.__connection.getSelection() is not CONNECTION_LOCAL: ++ hostname = self.__hostname.value() ++ else: ++ hostname = "local" + grid.setField(Label("Hostname:"), 0, 2, anchorRight = 1) +- grid.setField(Label(self.__hostname.value()), 1, 2, anchorLeft = 1) ++ grid.setField(Label(hostname), 1, 2, anchorLeft = 1) + grid.setField(Label("Autoconnect on Startup:"), 0, 3, anchorRight = 1) + label = "Yes" + if not self.__autoconnect.value(): label = "No" +diff --git a/nodeadmin/libvirtworker.py b/nodeadmin/libvirtworker.py +index 2998486..878b01c 100644 +--- a/nodeadmin/libvirtworker.py ++++ b/nodeadmin/libvirtworker.py +@@ -122,6 +122,12 @@ class LibvirtWorker: + domain = self.get_domain(name) + domain.undefine() + ++ def migrate_domain(self, name, target): ++ '''Migrates the specified domain to the target machine.''' ++ target_conn = libvirt.open(target) ++ virtmachine = self.get_domain(name) ++ virtmachine.migrate(target_conn, libvirt.VIR_MIGRATE_LIVE, None, None, 0) ++ + def list_networks(self, defined = True, started = True): + '''Lists all networks.''' + result = [] +diff --git a/nodeadmin/migratedomain.py b/nodeadmin/migratedomain.py +new file mode 100644 +index 0000000..8c8c268 +--- /dev/null ++++ b/nodeadmin/migratedomain.py +@@ -0,0 +1,81 @@ ++#!/usr/bin/env python ++# ++# migratedomain.py - Copyright (C) 2009 Red Hat, Inc. ++# Written by Darryl L. Pierce <dpierce at redhat.com> ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; version 2 of the License. ++# ++# 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 General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ++# MA 02110-1301, USA. A copy of the GNU General Public License is ++# also available at http://www.gnu.org/copyleft/gpl.html. ++ ++from snack import * ++from libvirtworker import LibvirtWorker ++from configscreen import * ++ ++LIST_DOMAINS = 1 ++SELECT_TARGET = 2 ++CONFIRM_PAGE = 3 ++ ++class MigrateDomainConfigScreen(DomainListConfigScreen): ++ def __init__(self): ++ DomainListConfigScreen.__init__(self, "Migrate Virtual Machine") ++ self.__configured = False ++ ++ def get_elements_for_page(self, screen, page): ++ if page is LIST_DOMAINS: return self.get_domain_list_page(screen) ++ elif page is SELECT_TARGET: return self.get_target_page(screen) ++ elif page is CONFIRM_PAGE: return self.get_confirm_page(screen) ++ ++ def page_has_next(self, page): ++ if page is LIST_DOMAINS: return self.has_selectable_domains() ++ else: return page < CONFIRM_PAGE ++ ++ def page_has_back(self, page): ++ return page < CONFIRM_PAGE ++ ++ def page_has_finish(self, page): ++ return page is CONFIRM_PAGE ++ ++ def validate_input(self, page, errors): ++ if page is LIST_DOMAINS: return self.get_selected_domain() is not None ++ elif page is SELECT_TARGET: ++ if self.__targets.current() is None: ++ errors.append("Please enter a target hostname or IP address.") ++ return False ++ elif page is CONFIRM_PAGE: ++ if not self.__confirm.value(): ++ errors.append("You must confirm migrating this virtual machine to proceed.") ++ return False ++ return True ++ ++ def process_input(self, page): ++ if page is CONFIRM_PAGE: ++ self.get_libvirt().migrate_domain(self.get_selected_domain(), self.__targets.current()) ++ self.set_finished() ++ ++ def get_target_page(self, screen): ++ self.__targets = Listbox(0) ++ for connection in self.get_virt_manager_config().get_connection_list(): ++ self.__targets.append(connection, connection) ++ return [Label("Select A Target Host"), ++ self.__targets] ++ ++ def get_confirm_page(self, screen): ++ self.__confirm = Checkbox("Confirm migrating this virtual machine.") ++ grid = Grid(1, 1) ++ grid.setField(self.__confirm, 0, 0) ++ return [grid] ++ ++def MigrateDomain(): ++ screen = MigrateDomainConfigScreen() ++ screen.start() +diff --git a/nodeadmin/nodemenu.py b/nodeadmin/nodemenu.py +index 16be89c..f213e09 100755 +--- a/nodeadmin/nodemenu.py ++++ b/nodeadmin/nodemenu.py +@@ -26,17 +26,19 @@ from startdomain import StartDomain + from stopdomain import StopDomain + from removedomain import RemoveDomain + from listdomains import ListDomains ++from migratedomain import MigrateDomain + from createuser import CreateUser + + import utils + import logging + +-ADD_DOMAIN = 1 +-START_DOMAIN = 2 +-STOP_DOMAIN = 3 +-REMOVE_DOMAIN = 4 +-LIST_DOMAINS = 5 +-CREATE_USER = 6 ++ADD_DOMAIN = 1 ++START_DOMAIN = 2 ++STOP_DOMAIN = 3 ++REMOVE_DOMAIN = 4 ++LIST_DOMAINS = 5 ++MIGRATE_DOMAIN = 6 ++CREATE_USER = 7 + + class NodeMenuScreen(MenuScreen): + def __init__(self): +@@ -48,15 +50,17 @@ class NodeMenuScreen(MenuScreen): + ("Stop A Virtual Machine", STOP_DOMAIN), + ("Remove A Virtual Machine", REMOVE_DOMAIN), + ("List All Virtual Machines", LIST_DOMAINS), ++ ("Migrate Virtual Machine", MIGRATE_DOMAIN), + ("Create A User", CREATE_USER)) + + def handle_selection(self, item): +- if item is ADD_DOMAIN: AddDomain() +- elif item is START_DOMAIN: StartDomain() +- elif item is STOP_DOMAIN: StopDomain() +- elif item is REMOVE_DOMAIN: RemoveDomain() +- elif item is LIST_DOMAINS: ListDomains() +- elif item is CREATE_USER: CreateUser() ++ if item is ADD_DOMAIN: AddDomain() ++ elif item is START_DOMAIN: StartDomain() ++ elif item is STOP_DOMAIN: StopDomain() ++ elif item is REMOVE_DOMAIN: RemoveDomain() ++ elif item is LIST_DOMAINS: ListDomains() ++ elif item is MIGRATE_DOMAIN: MigrateDomain() ++ elif item is CREATE_USER: CreateUser() + + def NodeMenu(): + screen = NodeMenuScreen() +diff --git a/nodeadmin/setup.py.in b/nodeadmin/setup.py.in +index 1e6e028..8b17487 100644 +--- a/nodeadmin/setup.py.in ++++ b/nodeadmin/setup.py.in +@@ -29,6 +29,7 @@ setup(name = "nodeadmin", + 'startvm = nodeadmin.startdomain:StartDomain', + 'stopvm = nodeadmin.stopdomain:StopDomain', + 'rmvm = nodeadmin.removedomain:RemoveDomain', ++ 'migratevm = nodeadmin.migratedomain:MigradeDomain', + 'createuser = nodeadmin.createuser:CreateUser', + 'listvms = nodeadmin.listdomains:ListDomains', + 'definenet = nodeadmin.definenet:DefineNetwork', +diff --git a/ovirt-node.spec.in b/ovirt-node.spec.in +index 23ca2bf..f46bd2b 100644 +--- a/ovirt-node.spec.in ++++ b/ovirt-node.spec.in +@@ -187,6 +187,7 @@ cd - + %{__install} -p -m0755 nodeadmin/adddomain.py %{buildroot}%{python_sitelib}/nodeadmin + %{__install} -p -m0644 nodeadmin/domainconfig.py %{buildroot}%{python_sitelib}/nodeadmin + %{__install} -p -m0755 nodeadmin/listdomains.py %{buildroot}%{python_sitelib}/nodeadmin ++%{__install} -p -m0755 nodeadmin/migratedomain.py %{buildroot}%{python_sitelib}/nodeadmin + %{__install} -p -m0755 nodeadmin/removedomain.py %{buildroot}%{python_sitelib}/nodeadmin + %{__install} -p -m0755 nodeadmin/startdomain.py %{buildroot}%{python_sitelib}/nodeadmin + %{__install} -p -m0755 nodeadmin/stopdomain.py %{buildroot}%{python_sitelib}/nodeadmin +@@ -380,6 +381,7 @@ fi + %{_bindir}/startvm + %{_bindir}/stopvm + %{_bindir}/rmvm ++%{_bindir}/migratevm + %{_bindir}/listvms + %{_bindir}/definenet + %{_bindir}/createnet +-- +1.6.5.2 + diff --git a/Makefile.am b/Makefile.am index de3bd18..e673aa4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,8 +28,10 @@ EXTRA_DIST = \ images/syslinux-vesa-splash.jpg \ nodeadmin/__init__.py \ nodeadmin/adddomain.py \ + nodeadmin/addhost.py \ nodeadmin/addpool.py \ nodeadmin/addvolume.py \ + nodeadmin/changehost.py \ nodeadmin/configscreen.py \ nodeadmin/createmeter.py \ nodeadmin/createnetwork.py \ @@ -38,6 +40,8 @@ EXTRA_DIST = \ nodeadmin/destroynetwork.py \ nodeadmin/domainconfig.py \ nodeadmin/halworker.py \ + nodeadmin/hostconnect.py \ + nodeadmin/hostmenu.py \ nodeadmin/libvirtworker.py \ nodeadmin/listdomains.py \ nodeadmin/listnetworks.py \ @@ -50,6 +54,7 @@ EXTRA_DIST = \ nodeadmin/nodemenu.py \ nodeadmin/poolconfig.py \ nodeadmin/removedomain.py \ + nodeadmin/removehost.py \ nodeadmin/removepool.py \ nodeadmin/removevolume.py \ nodeadmin/startdomain.py \ diff --git a/nodeadmin/addhost.py b/nodeadmin/addhost.py new file mode 100644 index 0000000..ef35b7d --- /dev/null +++ b/nodeadmin/addhost.py @@ -0,0 +1,129 @@ +# addhost.py - Copyright (C) 2009 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +from snack import * + +from configscreen import * + +DETAILS_PAGE = 1 +CONFIRM_PAGE = 2 + +HYPERVISOR_XEN = "xen" +HYPERVISOR_KVM = "kvm" + +HYPERVISORS = {HYPERVISOR_XEN : "Xen", + HYPERVISOR_KVM : "QEMU/KVM"} + +CONNECTION_LOCAL = "local" +CONNECTION_KERBEROS = "kerberos" +CONNECTION_SSL = "ssl" +CONNECTION_SSH = "ssh" + +CONNECTIONS = {CONNECTION_LOCAL : "Local", + CONNECTION_KERBEROS : "Remote Password or Kerberos", + CONNECTION_SSL : "Remote SSL/TLS with x509 certificate", + CONNECTION_SSH : "Remote tunnel over SSH"} + +class AddHostConfigScreen(ConfigScreen): + def __init__(self): + ConfigScreen.__init__(self, "Add A Remote Host") + self.__configured = False + + def get_elements_for_page(self, screen, page): + if page is DETAILS_PAGE: return self.get_details_page(screen) + elif page is CONFIRM_PAGE: return self.get_confirm_page(screen) + + def page_has_next(self, page): + return page < CONFIRM_PAGE + + def page_has_back(self, page): + return page > DETAILS_PAGE + + def page_has_finish(self, page): + return page is CONFIRM_PAGE + + def validate_input(self, page, errors): + if page is DETAILS_PAGE: + if len(self.__hostname.value()) > 0: + return True + else: + errors.append("You must enter a remote hostname.") + elif page is CONFIRM_PAGE: return True + return False + + def process_input(self, page): + if page is CONFIRM_PAGE: + hv = self.__hypervisor.getSelection() + conn = self.__connection.getSelection() + hostname = self.__hostname.value() + + if hv is HYPERVISOR_XEN: + if conn is CONNECTION_LOCAL: url = "xen:///" + elif conn is CONNECTION_KERBEROS: url = "xen+tcp:///" + hostname + "/" + elif conn is CONNECTION_SSL: url = "xen+tls:///" + hostname + "/" + elif conn is CONNECTION_SSH: url = "xen+ssh:///" + hostname + "/" + elif hv is HYPERVISOR_KVM: + if conn is CONNECTION_LOCAL: url = "qemu:///system" + elif conn is CONNECTION_KERBEROS: url = "qemu+tcp://" + hostname + "/system" + elif conn is CONNECTION_SSL: url = "qemu+tls://" + hostname + "/system" + elif conn is CONNECTION_SSH: url = "qemu+ssh://" + hostname + "/system" + + self.get_virt_manager_config().add_connection(url) + self.set_finished() + + def get_details_page(self, screen): + if not self.__configured: + self.__hypervisor = RadioBar(screen, ((HYPERVISORS[HYPERVISOR_XEN], HYPERVISOR_XEN, True), + (HYPERVISORS[HYPERVISOR_KVM], HYPERVISOR_KVM, False))) + self.__connection = RadioBar(screen, ((CONNECTIONS[CONNECTION_LOCAL], CONNECTION_LOCAL, True), + (CONNECTIONS[CONNECTION_KERBEROS], CONNECTION_KERBEROS, False), + (CONNECTIONS[CONNECTION_SSL], CONNECTION_SSL, False), + (CONNECTIONS[CONNECTION_SSH], CONNECTION_SSH, False))) + self.__hostname = Entry(50, "") + self.__autoconnect = Checkbox("Autoconnect on Startup") + self.__configured = True + grid = Grid(2, 4) + grid.setField(Label("Hypervisor:"), 0, 0, anchorRight = 1, anchorTop = 1) + grid.setField(self.__hypervisor, 1, 0, anchorLeft = 1) + grid.setField(Label("Connection:"), 0, 1, anchorRight = 1, anchorTop = 1) + grid.setField(self.__connection, 1, 1, anchorLeft = 1) + grid.setField(Label("Hostname:"), 0, 2, anchorRight = 1) + grid.setField(self.__hostname, 1, 2, anchorLeft = 1) + grid.setField(Label(""), 0, 3, anchorRight = 1) + grid.setField(self.__autoconnect, 1, 3, anchorLeft = 1) + return [Label("Add Connection"), + grid] + + def get_confirm_page(self, screen): + grid = Grid(2, 4) + grid.setField(Label("Hypervisor:"), 0, 0, anchorRight = 1) + grid.setField(Label(HYPERVISORS[self.__hypervisor.getSelection()]), 1, 0, anchorLeft = 1) + grid.setField(Label("Connection:"), 0, 1, anchorRight = 1) + grid.setField(Label(CONNECTIONS[self.__connection.getSelection()]), 1, 1, anchorLeft = 1) + grid.setField(Label("Hostname:"), 0, 2, anchorRight = 1) + grid.setField(Label(self.__hostname.value()), 1, 2, anchorLeft = 1) + grid.setField(Label("Autoconnect on Startup:"), 0, 3, anchorRight = 1) + label = "Yes" + if not self.__autoconnect.value(): label = "No" + grid.setField(Label(label), 1, 3, anchorLeft = 1) + return [Label("Confirm Connection"), + grid] + +def AddHost(): + screen = AddHostConfigScreen() + screen.start() diff --git a/nodeadmin/changehost.py b/nodeadmin/changehost.py new file mode 100644 index 0000000..23e6854 --- /dev/null +++ b/nodeadmin/changehost.py @@ -0,0 +1,58 @@ +# changehost.py - Copyright (C) 2009 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +from snack import * + +import logging +import libvirtworker +from configscreen import * + +CONNECTION_LIST_PAGE = 1 +CONNECTED_PAGE = 2 + +class ChangeHostConfigScreen(HostListConfigScreen): + def __init__(self): + HostListConfigScreen.__init__(self, "Change Host") + + def get_elements_for_page(self, screen, page): + if page is CONNECTION_LIST_PAGE: return self.get_connection_list_page(screen) + elif page is CONNECTED_PAGE: return self.get_connected_page(screen) + + def process_input(self, page): + if page is CONNECTION_LIST_PAGE: + logging.info("Changing libvirt connection to %s" % self.get_selected_connection()) + libvirtworker.set_default_url(self.get_selected_connection()) + self.get_libvirt().open_connection(self.get_selected_connection()) + elif page is CONNECTED_PAGE: self.set_finished() + + def page_has_next(self, page): + if page is CONNECTION_LIST_PAGE: return self.has_selectable_connections() + return False + + def page_has_back(self, page): + return page > CONNECTION_LIST_PAGE + + def page_has_finish(self, page): + return page is CONNECTED_PAGE + + def get_connected_page(self, screen): + return [Label("Connected to %s" % self.get_selected_connection())] + +def ChangeHost(): + screen = ChangeHostConfigScreen() + screen.start() diff --git a/nodeadmin/configscreen.py b/nodeadmin/configscreen.py index 7654697..02ab5b4 100644 --- a/nodeadmin/configscreen.py +++ b/nodeadmin/configscreen.py @@ -18,7 +18,7 @@ from snack import * from halworker import HALWorker -from libvirtworker import LibvirtWorker +from libvirtworker import * import traceback BACK_BUTTON = "back" @@ -35,6 +35,7 @@ class ConfigScreen: self.__finished = False self.__hal = HALWorker() self.__libvirt = LibvirtWorker() + self.__vm_config = VirtManagerConfig() def get_hal(self): return self.__hal @@ -42,6 +43,9 @@ class ConfigScreen: def get_libvirt(self): return self.__libvirt + def get_virt_manager_config(self): + return self.__vm_config + def set_finished(self): self.__finished = True @@ -231,3 +235,33 @@ class StorageListConfigScreen(ConfigScreen): def has_selectable_volumes(self): return self.__has_volumes + +class HostListConfigScreen(ConfigScreen): + '''Provides a base class for working with lists of libvirt hosts.''' + + def __init__(self, title): + ConfigScreen.__init__(self, title) + + def get_connection_list_page(self, screen): + connections = self.get_virt_manager_config().get_connection_list() + result = None + + if len(connections) > 0: + self.__has_connections = True + self.__connection_list = Listbox(0) + for connection in connections: + self.__connection_list.append(connection, connection) + result = self.__connection_list + else: + self.__has_connections = False + result = Label("There are no defined connections.") + grid = Grid(1, 1) + grid.setField(result, 0, 0) + return [Label("Host List"), + grid] + + def get_selected_connection(self): + return self.__connection_list.current() + + def has_selectable_connections(self): + return self.__has_connections diff --git a/nodeadmin/definenet.py b/nodeadmin/definenet.py index 4aa37d5..6dff18f 100644 --- a/nodeadmin/definenet.py +++ b/nodeadmin/definenet.py @@ -20,6 +20,7 @@ from snack import * from IPy import IP import traceback import logging +import re from configscreen import ConfigScreen from networkconfig import NetworkConfig diff --git a/nodeadmin/hostconnect.py b/nodeadmin/hostconnect.py new file mode 100644 index 0000000..a1be569 --- /dev/null +++ b/nodeadmin/hostconnect.py @@ -0,0 +1,29 @@ +# hostconnect.py - Copyright (C) 2009 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +from snack import * + +from configscreen import * + +class HostConnectConfigScreen(ConfigScreen): + def __init__(self): + ConfigScree + +def HostConnect(): + screen = HostConnectConfigScreen() + screen.start() diff --git a/nodeadmin/hostmenu.py b/nodeadmin/hostmenu.py new file mode 100644 index 0000000..4054d6b --- /dev/null +++ b/nodeadmin/hostmenu.py @@ -0,0 +1,46 @@ +# hostmenu.py - Copyright (C) 2009 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +from snack import * + +from menuscreen import MenuScreen +from changehost import ChangeHost +from addhost import AddHost +from removehost import RemoveHost + +SELECT_HOST = 1 +ADD_HOST = 2 +REMOVE_HOST = 3 + +class HostMenuScreen(MenuScreen): + def __init__(self): + MenuScreen.__init__(self, "Host Menu Screen") + + def get_menu_items(self): + return (("Select A Host", SELECT_HOST), + ("Add A Host", ADD_HOST), + ("Remove A Host", REMOVE_HOST)) + + def handle_selection(self, item): + if item is SELECT_HOST: ChangeHost() + elif item is ADD_HOST: AddHost() + elif item is REMOVE_HOST: RemoveHost() + +def HostMenu(): + screen = HostMenuScreen() + screen.start() diff --git a/nodeadmin/libvirtworker.py b/nodeadmin/libvirtworker.py index f31266c..15d1c58 100644 --- a/nodeadmin/libvirtworker.py +++ b/nodeadmin/libvirtworker.py @@ -21,15 +21,60 @@ import libvirt import os import virtinst import utils +import logging from domainconfig import DomainConfig DEFAULT_POOL_TARGET_PATH="/var/lib/libvirt/images" +DEFAULT_URL="qemu:///system" + +default_url = DEFAULT_URL + +def set_default_url(url): + logging.info("Changing DEFAULT_URL to %s" % url) + global default_url + + default_url = url + +def get_default_url(): + logging.info("Returning default URL of %s" % default_url) + return default_url + +class VirtManagerConfig: + def __init__(self, filename = "/etc/remote-libvirt.conf"): + self.__filename = filename + + def get_connection_list(self): + result = [] + if os.path.exists(self.__filename): + input = file(self.__filename, "r") + for entry in input: result.append(entry[0:-1]) + return result + + def add_connection(self, connection): + connections = self.get_connection_list() + if connections.count(connection) is 0: + connections.append(connection) + self._save_connections(connections) + + def remove_connection(self, connection): + connections = self.get_connection_list() + if connections.count(connection) > 0: + connections.remove(connection) + self._save_connections(connections) + + def _save_connections(self, connections): + output = file(self.__filename, "w") + for entry in connections: + print >> output, entry + output.close class LibvirtWorker: '''Provides utilities for interfacing with libvirt.''' - def __init__(self, url = "qemu:///system"): - self.__conn = libvirt.open(url) + def __init__(self, url = None): + if url is None: url = get_default_url() + logging.info("Connecting to libvirt: %s" % url) + self.open_connection(url) self.__capabilities = virtinst.CapabilitiesParser.parse(self.__conn.getCapabilities()) self.__net = virtinst.VirtualNetworkInterface(conn = self.__conn) self.__net.setup(self.__conn) @@ -39,6 +84,10 @@ class LibvirtWorker: '''Returns the underlying connection.''' return self.__conn + def open_connection(self, url): + '''Lets the user change the url for the connection.''' + self.__conn = libvirt.open(url) + def list_domains(self, defined = True, started = True): '''Lists all domains.''' result = [] diff --git a/nodeadmin/mainmenu.py b/nodeadmin/mainmenu.py index 52d9298..9c435fd 100755 --- a/nodeadmin/mainmenu.py +++ b/nodeadmin/mainmenu.py @@ -23,14 +23,16 @@ from menuscreen import MenuScreen from nodemenu import NodeMenu from netmenu import NetworkMenu from storagemenu import StoragePoolMenu +from hostmenu import HostMenu import utils import logging -NODE_MENU = 1 -NETWORK_MENU = 2 -STORAGE_MENU = 3 -EXIT_CONSOLE = 4 +NODE_MENU = 1 +NETWORK_MENU = 2 +STORAGE_MENU = 3 +HOST_MENU = 4 +EXIT_CONSOLE = 99 class MainMenuScreen(MenuScreen): def __init__(self): @@ -39,12 +41,14 @@ class MainMenuScreen(MenuScreen): def get_menu_items(self): return (("Node Administration", NODE_MENU), ("Network Administration", NETWORK_MENU), - ("Storage Pool Administration", STORAGE_MENU)) - - def handle_selection(self, item): - if item is NODE_MENU: NodeMenu() - elif item is NETWORK_MENU: NetworkMenu() - elif item is STORAGE_MENU: StoragePoolMenu() + ("Storage Pool Administration", STORAGE_MENU), + ("Host Administration", HOST_MENU)) + + def handle_selection(self, page): + if page is NODE_MENU: NodeMenu() + elif page is NETWORK_MENU: NetworkMenu() + elif page is STORAGE_MENU: StoragePoolMenu() + elif page is HOST_MENU: HostMenu() def MainMenu(): screen = MainMenuScreen() diff --git a/nodeadmin/removehost.py b/nodeadmin/removehost.py new file mode 100644 index 0000000..cf3c46c --- /dev/null +++ b/nodeadmin/removehost.py @@ -0,0 +1,66 @@ +# removehost.py - Copyright (C) 2009 Red Hat, Inc. +# Written by Darryl L. Pierce <dpierce at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +from snack import * + +from configscreen import * + +SELECT_HOST_PAGE = 1 +CONFIRM_REMOVE_PAGE = 2 + +class RemoveHostConfigScreen(HostListConfigScreen): + def __init__(self): + HostListConfigScreen.__init__(self, "Remove Host Connection") + + def get_elements_for_page(self, screen, page): + if page is SELECT_HOST_PAGE: return self.get_connection_list_page(screen) + elif page is CONFIRM_REMOVE_PAGE: return self.get_confirm_remove_page(screen) + + def page_has_next(self, page): + return page is SELECT_HOST_PAGE and self.has_selectable_connections() + + def page_has_back(self, page): + return page is CONFIRM_REMOVE_PAGE + + def page_has_finish(self, page): + return page is CONFIRM_REMOVE_PAGE + + def validate_input(self, page, errors): + if page is SELECT_HOST_PAGE: return True + elif page is CONFIRM_REMOVE_PAGE: + if self.__confirm.value(): + return True + else: + errors.append("You must confirm removing the connection.") + return False + + def process_input(self, page): + if page is CONFIRM_REMOVE_PAGE: + self.get_virt_manager_config().remove_connection(self.get_selected_connection()) + self.set_finished() + + def get_confirm_remove_page(self, screen): + self.__confirm = Checkbox("Remove this connection: %s" % self.get_selected_connection(), 0) + grid = Grid(1, 1) + grid.setField(self.__confirm, 0, 0) + return [Label("Remove Host Connection"), + grid] + +def RemoveHost(): + screen = RemoveHostConfigScreen() + screen.start() diff --git a/ovirt-node.spec.in b/ovirt-node.spec.in index 325fecc..f056f3f 100644 --- a/ovirt-node.spec.in +++ b/ovirt-node.spec.in @@ -198,6 +198,11 @@ cd - %{__install} -p -m0755 nodeadmin/destroynetwork.py %{buildroot}%{python_sitelib}/nodeadmin %{__install} -p -m0755 nodeadmin/undefinenetwork.py %{buildroot}%{python_sitelib}/nodeadmin +%{__install} -p -m0755 nodeadmin/addhost.py %{buildroot}%{python_sitelib}/nodeadmin +%{__install} -p -m0644 nodeadmin/changehost.py %{buildroot}%{python_sitelib}/nodeadmin +%{__install} -p -m0755 nodeadmin/hostmenu.py %{buildroot}%{python_sitelib}/nodeadmin +%{__install} -p -m0755 nodeadmin/removehost.py %{buildroot}%{python_sitelib}/nodeadmin + %{__install} -p -m0755 nodeadmin/createuser.py %{buildroot}%{python_sitelib}/nodeadmin %{__install} -p -m0644 nodeadmin/halworker.py %{buildroot}%{python_sitelib}/nodeadmin -- 1.6.5.2
Darryl L. Pierce
2009-Dec-18 16:13 UTC
[Ovirt-devel] Rebased patches, fixed a rebasing problem...
On Tue, Dec 08, 2009 at 04:13:52PM -0500, Darryl L. Pierce wrote:> The previous patch set had an error and one development branch's changes > showed up twice, and two different change sets. So it was missing the bulk > of the configuration work. This patch set fixes that.If there are no objections, I'm going to push these upstream now and we'll fix any issues that come up afterward. -- Darryl L. Pierce, Sr. Software Engineer @ Red Hat, Inc. Delivering value year after year. Red Hat ranks #1 in value among software vendors. http://www.redhat.com/promo/vendor/ -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 198 bytes Desc: not available URL: <http://listman.redhat.com/archives/ovirt-devel/attachments/20091218/d6e18ca6/attachment.sig>