[Ovirt-devel] [PATCH 1/2] Users can now work with remote libvirt hosts.
Darryl L. Pierce
dpierce at redhat.com
Tue Dec 8 21:13:53 UTC 2009
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
More information about the ovirt-devel
mailing list