This set of patches have been rebased since the originals have grown stale.
Darryl L. Pierce
2009-Dec-03 21:42 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> --- 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