[Ovirt-devel] [PATCH node] Introduces the node-admin toolset.
Darryl L. Pierce
dpierce at redhat.com
Wed Sep 16 12:34:20 UTC 2009
Defines a primary entry point that displays a menu of options that user
can use to administer the managed node. It leverages the code from
virtinst-python to do the heavy lifting and just provides a front end
for collecting user data.
Created a new configuration class to be used for all configuration
screens.
The user can select to create a node. They are then walked through the
steps to create a domain similar to virt-manager.
The user can create a defined domain.
The user can destroy a created domain.
The user can undefine a defined domain.
The user can list all domains on the system.
The user can create a new user account.
When the RPM is created, a separate entry point is created for each
node admin command. This allows the commands to be invoked individually
as well as from the main menu.
Signed-off-by: Darryl L. Pierce <dpierce at redhat.com>
---
.gitignore | 1 +
Makefile.am | 16 ++
configure.ac | 1 +
nodeadmin/__init__.py | 20 ++
nodeadmin/configscreen.py | 150 ++++++++++++++
nodeadmin/createdomain.py | 68 +++++++
nodeadmin/createuser.py | 106 ++++++++++
nodeadmin/definedomain.py | 470 +++++++++++++++++++++++++++++++++++++++++++
nodeadmin/destroydomain.py | 66 ++++++
nodeadmin/domainconfig.py | 217 ++++++++++++++++++++
nodeadmin/halworker.py | 37 ++++
nodeadmin/libvirtworker.py | 276 +++++++++++++++++++++++++
nodeadmin/listdomains.py | 68 +++++++
nodeadmin/mainmenu.py | 74 +++++++
nodeadmin/nodeadmin.py | 29 +++
nodeadmin/setup.py.in | 34 +++
nodeadmin/undefinedomain.py | 83 ++++++++
nodeadmin/userworker.py | 38 ++++
ovirt-node.spec.in | 38 ++++
19 files changed, 1792 insertions(+), 0 deletions(-)
create mode 100644 nodeadmin/__init__.py
create mode 100644 nodeadmin/configscreen.py
create mode 100755 nodeadmin/createdomain.py
create mode 100755 nodeadmin/createuser.py
create mode 100755 nodeadmin/definedomain.py
create mode 100755 nodeadmin/destroydomain.py
create mode 100644 nodeadmin/domainconfig.py
create mode 100644 nodeadmin/halworker.py
create mode 100644 nodeadmin/libvirtworker.py
create mode 100755 nodeadmin/listdomains.py
create mode 100755 nodeadmin/mainmenu.py
create mode 100755 nodeadmin/nodeadmin.py
create mode 100644 nodeadmin/setup.py.in
create mode 100755 nodeadmin/undefinedomain.py
create mode 100644 nodeadmin/userworker.py
diff --git a/.gitignore b/.gitignore
index 26a0210..19b15d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,4 @@ missing
stamp-h1
ovirt-node*.gz
ovirt-node.spec
+*.pyc
diff --git a/Makefile.am b/Makefile.am
index 419cdf1..2d195c0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -26,6 +26,22 @@ EXTRA_DIST = \
ovirt-node-selinux.fc \
images/grub-splash.xpm.gz \
images/syslinux-vesa-splash.jpg \
+ nodeadmin/__init__.py \
+ nodeadmin/configscreen.py \
+ nodeadmin/createuser.py \
+ nodeadmin/destroydomain.py \
+ nodeadmin/halworker.py \
+ nodeadmin/libvirtworker.py \
+ nodeadmin/userworker.py \
+ nodeadmin/mainmenu.py \
+ nodeadmin/undefinedomain.py \
+ nodeadmin/createdomain.py \
+ nodeadmin/definedomain.py \
+ nodeadmin/domainconfig.py \
+ nodeadmin/listdomains.py \
+ nodeadmin/nodeadmin.py \
+ nodeadmin/setup.py \
+ nodeadmin/utils.py \
scripts/collectd.conf.in \
scripts/ovirt \
scripts/ovirt-awake \
diff --git a/configure.ac b/configure.ac
index b60afeb..780b757 100644
--- a/configure.ac
+++ b/configure.ac
@@ -8,6 +8,7 @@ test x"$ac_ct_CC:$CFLAGS" = 'xgcc:-g -O2' \
&& CFLAGS="$CFLAGS -Wshadow -Wall -Werror"
AC_CONFIG_FILES([Makefile
+ nodeadmin/setup.py
gptsync/Makefile
ovirt-node.spec
])
diff --git a/nodeadmin/__init__.py b/nodeadmin/__init__.py
new file mode 100644
index 0000000..1f3c72c
--- /dev/null
+++ b/nodeadmin/__init__.py
@@ -0,0 +1,20 @@
+# __init__.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 nodeadmin import NodeAdmin
+
diff --git a/nodeadmin/configscreen.py b/nodeadmin/configscreen.py
new file mode 100644
index 0000000..0282eee
--- /dev/null
+++ b/nodeadmin/configscreen.py
@@ -0,0 +1,150 @@
+# configscreen.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 halworker import HALWorker
+from libvirtworker import LibvirtWorker
+import traceback
+
+BACK_BUTTON = "back"
+NEXT_BUTTON = "next"
+CANCEL_BUTTON = "cancel"
+FINISH_BUTTON = "finish"
+
+class ConfigScreen:
+ '''Enables the creation of navigable, multi-paged configuration screens.'''
+
+ def __init__(self, title):
+ self.__title = title
+ self.__current_page = 1
+ self.__finished = False
+ self.__hal = HALWorker()
+ self.__libvirt = LibvirtWorker()
+
+ def get_hal(self):
+ return self.__hal
+
+ def get_libvirt(self):
+ return self.__libvirt
+
+ def set_finished(self):
+ self.__finished = True
+
+ def get_elements_for_page(self, screen, page):
+ return []
+
+ def page_has_next(self, page):
+ return False
+
+ def page_has_finish(self, page):
+ return False
+
+ def get_back_page(self, page):
+ if page > 1: return page - 1
+ return page
+
+ def go_back(self):
+ self.__current_page = self.get_back_page(self.__current_page)
+
+ def get_next_page(self, page):
+ return page + 1
+
+ def go_next(self):
+ self.__current_page = self.get_next_page(self.__current_page)
+
+ def validate_input(self, page, errors):
+ return True
+
+ def process_input(self, page):
+ return
+
+ def start(self):
+ active = True
+ while active and (self.__finished == False):
+ screen = SnackScreen()
+ gridform = GridForm(screen, self.__title, 1, 4)
+ elements = self.get_elements_for_page(screen, self.__current_page)
+ current_element = 0
+ for element in elements:
+ gridform.add(element, 0, current_element)
+ current_element += 1
+ # create the navigation buttons
+ buttons = []
+ if self.__current_page > 1: buttons.append(["Back", BACK_BUTTON, "F11"])
+ if self.page_has_next(self.__current_page): buttons.append(["Next", NEXT_BUTTON, "F12"])
+ if self.page_has_finish(self.__current_page): buttons.append(["Finish", FINISH_BUTTON, "F10"])
+ buttons.append(["Cancel", CANCEL_BUTTON, "ESC"])
+ buttonbar = ButtonBar(screen, buttons)
+ gridform.add(buttonbar, 0, current_element, growx = 1)
+ current_element += 1
+ try:
+ result = gridform.runOnce()
+ pressed = buttonbar.buttonPressed(result)
+ if pressed == BACK_BUTTON:
+ self.go_back()
+ elif pressed == NEXT_BUTTON or pressed == FINISH_BUTTON:
+ errors = []
+ if self.validate_input(self.__current_page, errors):
+ self.process_input(self.__current_page)
+ self.go_next()
+ else:
+ error_text = ""
+ for error in errors:
+ error_text += "%s\n" % error
+ ButtonChoiceWindow(screen,
+ "There Were Errors",
+ error_text,
+ buttons = ["OK"])
+ elif pressed == CANCEL_BUTTON:
+ active = False
+ except Exception, error:
+ ButtonChoiceWindow(screen,
+ "An Exception Has Occurred",
+ str(error) + "\n" + traceback.format_exc(),
+ buttons = ["OK"])
+ screen.popWindow()
+ screen.finish()
+
+class DomainListConfigScreen(ConfigScreen):
+ '''Provides a base class for all config screens that require a domain list.'''
+
+ def __init__(self, title):
+ ConfigScreen.__init__(self, title)
+
+ def get_domain_list_page(self, screen, defined=True, created=True):
+ domains = self.get_libvirt().list_domains(defined, created)
+ result = None
+
+ if len(domains) > 0:
+ self.__has_domains = True
+ self.__domain_list = Listbox(0)
+ for name in self.get_libvirt().list_domains(defined, created):
+ self.__domain_list.append(name, name)
+ result = [self.__domain_list]
+ else:
+ self.__has_domains = False
+ grid = Grid(1, 1)
+ grid.setField(Label("There are no domains available."), 0, 0)
+ result = [grid]
+ return result
+
+ def get_selected_domain(self):
+ return self.__domain_list.current()
+
+ def has_selectable_domains(self):
+ return self.__has_domains
diff --git a/nodeadmin/createdomain.py b/nodeadmin/createdomain.py
new file mode 100755
index 0000000..b73a09e
--- /dev/null
+++ b/nodeadmin/createdomain.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+#
+# createdomain.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 CreateDomainConfigScreen(DomainListConfigScreen):
+ LIST_PAGE = 1
+ CREATE_PAGE = 2
+
+ def __init__(self):
+ DomainListConfigScreen.__init__(self, "Create A Domain")
+
+ def get_elements_for_page(self, screen, page):
+ if page is self.LIST_PAGE:
+ return self.get_domain_list_page(screen, created = False)
+ elif page is self.CREATE_PAGE:
+ return self.get_create_domain_page(screen)
+
+ def page_has_next(self, page):
+ if page is self.LIST_PAGE: return self.has_selectable_domains()
+ return False
+
+ def page_has_back(self, page):
+ if page is self.CREATE_PAGE: return True
+ return False
+
+ def validate_input(self, page, errors):
+ if page is self.LIST_PAGE:
+ if self.get_selected_domain() is not None:
+ domain = self.get_selected_domain()
+ try:
+ self.get_libvirt().create_domain(domain)
+ return True
+ except Exception, error:
+ errors.append("There was an error creating the domain: %s" % domain)
+ errors.append(str(error))
+ else:
+ errors.append("You must first select a domain to create.")
+
+ def process_input(self, page):
+ print "foo"
+
+ def get_create_domain_page(self, screen):
+ grid = Grid(1, 1)
+ grid.setField(Label("%s was successfully created." % self.get_selected_domain()), 0, 0)
+ return [grid]
+
+def CreateDomain():
+ screen = CreateDomainConfigScreen()
+ screen.start()
diff --git a/nodeadmin/createuser.py b/nodeadmin/createuser.py
new file mode 100755
index 0000000..dbc4626
--- /dev/null
+++ b/nodeadmin/createuser.py
@@ -0,0 +1,106 @@
+#!/usr/bin/env python
+#
+# createuser.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 ConfigScreen
+from userworker import UserWorker
+
+import libuser
+
+DETAILS_PAGE = 1
+CONFIRM_PAGE = 2
+
+class CreateUserConfigScreen(ConfigScreen):
+ def __init__(self):
+ ConfigScreen.__init__(self, "Create A User Account")
+ self.__username = None
+ self.__useradmin = libuser.admin()
+ self.__user_worker = UserWorker()
+
+ 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 validate_input(self, page, errors):
+ if page is DETAILS_PAGE:
+ if len(self.__username.value()) > 0:
+ name = self.__username.value()
+ if self.__useradmin.lookupUserByName(name) is None:
+ if len(self.__password.value()) > 0:
+ if self.__password.value() == self.__confirm.value():
+ return True
+ else:
+ errors.append("Passwords do not match.")
+ else:
+ errors.append("You must enter a password.")
+ else:
+ errors.append("User %s already exists." % name)
+ else:
+ errors.append("You must enter a username.")
+ self.__confirm.value()
+ return False
+
+ def process_input(self, page):
+ if page is CONFIRM_PAGE:
+ self.__user_worker.create_user(self.__username.value(),
+ self.__password.value(),
+ "wheel" if self.__adminuser.value() else None)
+ self.set_finished()
+
+ def page_has_next(self, page):
+ return (page is DETAILS_PAGE)
+
+ def page_has_back(self, page):
+ return (page is CONFIRM_PAGE)
+
+ def page_has_finish(self, page):
+ return (page is CONFIRM_PAGE)
+
+ def get_details_page(self, screen):
+ if self.__username is None:
+ self.__username = Entry(50, "")
+ self.__password = Entry(50, "", password = 1)
+ self.__confirm = Entry(50, "", password = 1)
+ self.__adminuser = Checkbox("This user is an administrator", False)
+ grid = Grid(2, 4)
+ grid.setField(Label("Username:"), 0, 0, anchorRight = 1)
+ grid.setField(self.__username, 1, 0, anchorLeft = 1)
+ grid.setField(Label("Password:"), 0, 1, anchorRight = 1)
+ grid.setField(self.__password, 1, 1, anchorLeft = 1)
+ grid.setField(Label("Confirm password:"), 0, 2, anchorRight = 1)
+ grid.setField(self.__confirm, 1, 2, anchorLeft = 1)
+ grid.setField(Label(" "), 0, 3)
+ grid.setField(self.__adminuser, 1, 3, anchorLeft = 1)
+ return [Label("Enter The User Details"),
+ grid]
+
+ def get_confirm_page(self, screen):
+ grid = Grid(1, 2)
+ grid.setField(Label("Username: %s" % self.__username.value()), 0, 0)
+ admin_label = "is not"
+ if self.__adminuser.value():
+ admin_label = "is"
+ grid.setField(Label("This user %s an administrator." % admin_label), 0, 1)
+ return [Label("Create this user account?"),
+ grid]
+
+def CreateUser():
+ screen = CreateUserConfigScreen()
+ screen.start()
diff --git a/nodeadmin/definedomain.py b/nodeadmin/definedomain.py
new file mode 100755
index 0000000..6a6612c
--- /dev/null
+++ b/nodeadmin/definedomain.py
@@ -0,0 +1,470 @@
+#!/usr/bin/env python
+#
+# definedomain.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 os
+from domainconfig import DomainConfig
+from configscreen import ConfigScreen
+import urlgrabber.progress as progress
+import utils
+import logging
+
+from virtinst import *
+
+VM_DETAILS_PAGE = 1
+LOCAL_INSTALL_PAGE = 2
+SELECT_CDROM_PAGE = 3
+SELECT_ISO_PAGE = 4
+NETWORK_INSTALL_PAGE = 10
+OS_TYPE_PAGE = 11
+OS_VARIANT_PAGE = 12
+RAM_CPU_PAGE = 13
+ENABLE_STORAGE_PAGE = 14
+LOCAL_STORAGE_PAGE = 15
+MANAGED_STORAGE_PAGE = 16
+BRIDGE_PAGE = 17
+VIRT_DETAILS_PAGE = 18
+CONFIRM_PAGE = 19
+
+LOCATION="location"
+KICKSTART="kickstart"
+KERNELOPTS="kernel.options"
+OS_TYPE="os.type"
+OS_VARIANT="os.variant"
+MEMORY="memory"
+CPUS="cpus"
+
+class DummyMeter(progress.BaseMeter):
+ def _do_start(self, now = None):
+ logging.info("Starting...")
+
+ def _do_end(self, amount_read, now = None):
+ logging.info("Ending: read=%d" % amount_read)
+
+ def _do_update(self, amount_read, now = None):
+ logging.info("Update: read=%d" % amount_read)
+
+class DomainConfigScreen(ConfigScreen):
+ def __init__(self):
+ ConfigScreen.__init__(self, "Create A New Virtual Machine")
+ self.__config = DomainConfig()
+ self.__config.set_architecture(self.get_libvirt().get_default_architecture())
+ self.__config.set_virt_type(self.get_libvirt().get_default_virt_type())
+
+ def get_elements_for_page(self, screen, page):
+ if page == VM_DETAILS_PAGE: return self.get_vm_details_page(screen)
+ elif page == LOCAL_INSTALL_PAGE: return self.get_local_install_page(screen)
+ elif page == SELECT_CDROM_PAGE: return self.get_select_cdrom_page(screen)
+ elif page == SELECT_ISO_PAGE: return self.get_select_iso_page(screen)
+ elif page == NETWORK_INSTALL_PAGE: return self.get_network_install_page(screen)
+ elif page == OS_TYPE_PAGE: return self.get_os_type_page(screen)
+ elif page == OS_VARIANT_PAGE: return self.get_os_variant_page(screen)
+ elif page == RAM_CPU_PAGE: return self.get_ram_and_cpu_page(screen)
+ elif page == ENABLE_STORAGE_PAGE: return self.get_enable_storage_page(screen)
+ elif page == LOCAL_STORAGE_PAGE: return self.get_local_storage_page(screen)
+ elif page == MANAGED_STORAGE_PAGE: return self.get_managed_storage_page(screen)
+ elif page == BRIDGE_PAGE: return self.get_bridge_page(screen)
+ elif page == VIRT_DETAILS_PAGE: return self.get_virt_details_page(screen)
+ elif page == CONFIRM_PAGE: return self.get_confirm_page(screen)
+ return []
+
+ def validate_input(self, page, errors):
+ if page == VM_DETAILS_PAGE:
+ if len(self.__guest_name.value()) > 0:
+ if self.get_libvirt().domain_exists(self.__guest_name.value()):
+ errors.append("Guest name '%s' is already in use." % self.__guest_name.value())
+ else:
+ return True
+ else:
+ errors.append("Guest name must be a string between 0 and 50 characters.")
+ elif page == LOCAL_INSTALL_PAGE:
+ if self.__install_source.getSelection() == DomainConfig.INSTALL_SOURCE_CDROM:
+ return True
+ elif self.__install_source.getSelection() == DomainConfig.INSTALL_SOURCE_ISO:
+ return True
+ elif page == SELECT_CDROM_PAGE:
+ if self.__install_media.getSelection() != None:
+ if len(self.get_hal().list_installable_volumes()) == 0:
+ errors.append("No installable media is available.")
+ else:
+ return True
+ else:
+ errors.append("You must select an install media.")
+ elif page == SELECT_ISO_PAGE:
+ if len(self.__iso_path.value()) > 0:
+ if os.path.exists(self.__iso_path.value()):
+ if os.path.isfile(self.__iso_path.value()):
+ return True
+ else:
+ errors.append("%s is not a file." % self.__iso_path.value())
+ else:
+ errors.append("No such install media exists:")
+ errors.append(self.__iso_path.value())
+ else:
+ errors.append("An install media selection is required.")
+ elif page == NETWORK_INSTALL_PAGE:
+ if len(self.__install_url.value()) > 0:
+ return True
+ else:
+ errors.append("An install tree is required.")
+ elif page == OS_TYPE_PAGE: return True
+ elif page == OS_VARIANT_PAGE: return True
+ elif page == RAM_CPU_PAGE:
+ if (len(self.__memory.value()) > 0 and len(self.__cpus.value()) > 0) \
+ and (int(self.__memory.value()) > 0 and int(self.__cpus.value()) > 0):
+ return True
+ else:
+ if len(self.__memory.value()) == 0:
+ errors.append("A value must be entered for memory.")
+ elif int(self.__memory.value()) <= 0:
+ errors.append("A positive integer value must be entered for memory.")
+ if len(self.__cpus.value()) == 0:
+ errors.append("A value must be entered for CPUs.")
+ elif int(self.__cpus.value()) <= 0:
+ errors.append("A positive integer value must be entered for memory.")
+ elif page == ENABLE_STORAGE_PAGE: return True
+ elif page == LOCAL_STORAGE_PAGE:
+ if len(self.__storage_size.value()) > 0:
+ if float(self.__storage_size.value()) > 0:
+ return True
+ else:
+ errors.append("A positive value must be entered for the storage size.")
+ else:
+ errors.append("A value must be entered for the storage size.")
+ elif page == MANAGED_STORAGE_PAGE:
+ if self.__existing_storage.getSelection() is not None:
+ return True
+ else:
+ errors.append("Please select a storage volume.")
+ elif page == BRIDGE_PAGE:
+ if self.__network_bridges.getSelection() != None:
+ if len(self.__mac_address.value()) > 0:
+ # TODO: regex check the format
+ return True
+ else:
+ errors.append("MAC address must be supplied.")
+ else:
+ errors.append("A network bridge must be selected.")
+ elif page == VIRT_DETAILS_PAGE:
+ if self.__virt_types.getSelection() != None and self.__architectures.getSelection() != None:
+ return True
+ if self.__virt_types.getSelection() is None:
+ errors.append("Please select a virtualization type.")
+ if self.__architectures.getSelection() is None:
+ errors.append("Please selection an architecture.")
+ elif page == CONFIRM_PAGE: return True
+ return False
+
+ def process_input(self, page):
+ if page == VM_DETAILS_PAGE:
+ self.__config.set_guest_name(self.__guest_name.value())
+ self.__config.set_install_type(self.__install_type.getSelection())
+ elif page == LOCAL_INSTALL_PAGE:
+ self.__config.set_use_cdrom_source(self.__install_source.getSelection() == DomainConfig.INSTALL_SOURCE_CDROM)
+ elif page == SELECT_CDROM_PAGE:
+ self.__config.set_install_media(self.__install_media.getSelection())
+ elif page == SELECT_ISO_PAGE:
+ self.__config.set_iso_path(self.__iso_path.value())
+ elif page == NETWORK_INSTALL_PAGE:
+ self.__config.set_install_url(self.__install_url.value())
+ self.__config.set_kickstart_url(self.__kickstart_url.value())
+ self.__config.set_kernel_options(self.__kernel_options.value())
+ elif page == OS_TYPE_PAGE:
+ self.__config.set_os_type(self.__os_types.getSelection())
+ elif page == OS_VARIANT_PAGE:
+ self.__config.set_os_variant(self.__os_variants.getSelection())
+ elif page == RAM_CPU_PAGE:
+ self.__config.set_memory(int(self.__memory.value()))
+ self.__config.set_cpus(int(self.__cpus.value()))
+ elif page == ENABLE_STORAGE_PAGE:
+ self.__config.set_enable_storage(self.__enable_storage.value())
+ if self.__storage_type.getSelection() == DomainConfig.NEW_STORAGE:
+ self.__config.set_use_local_storage(True)
+ elif self.__storage_type.getSelection() == Node.STORAGE_TYPE_EXISTING:
+ self.__config.set_use_local_storage(False)
+ elif page == LOCAL_STORAGE_PAGE:
+ self.__config.set_storage_size(float(self.__storage_size.value()))
+ self.__config.set_allocate_storage(self.__allocate_storage.value())
+ elif page == MANAGED_STORAGE_PAGE:
+ self.__config.set_use_local_storage(False)
+ self.__config.set_existing_storage(self.__existing_storage.getSelection())
+ self.__config.set_storage_size(self.get_libvirt().get_storage_size(self.__existing_storage.getSelection()))
+ elif page == BRIDGE_PAGE:
+ self.__config.set_network_bridge(self.__network_bridges.getSelection())
+ elif page == VIRT_DETAILS_PAGE:
+ self.__config.set_virt_type(self.__virt_types.getSelection())
+ self.__config.set_architecture(self.__architectures.getSelection())
+ elif page == CONFIRM_PAGE:
+ self.get_libvirt().define_domain(self.__config, DummyMeter())
+ self.set_finished()
+
+ def get_back_page(self, page):
+ result = page
+ if page == OS_TYPE_PAGE:
+ install_type = self.__config.get_install_type()
+ if install_type == DomainConfig.LOCAL_INSTALL:
+ if self.__config.get_use_cdrom_source():
+ result = SELECT_CDROM_PAGE
+ else:
+ result = SELECT_ISO_PAGE
+ elif install_type == DomainConfig.NETWORK_INSTALL:
+ result = NETWORK_INSTALL_PAGE
+ elif install_type == DomainConfig.PXE_INSTALL:
+ result = VM_DETAILS_PAGE
+ elif page == LOCAL_STORAGE_PAGE or page == MANAGED_STORAGE_PAGE:
+ result = ENABLE_STORAGE_PAGE
+ elif page == NETWORK_INSTALL_PAGE:
+ result = VM_DETAILS_PAGE
+ elif page == SELECT_CDROM_PAGE or page == SELECT_ISO_PAGE:
+ result = LOCAL_INSTALL_PAGE
+ elif page == BRIDGE_PAGE:
+ if self.__config.get_use_local_storage():
+ result = LOCAL_STORAGE_PAGE
+ else:
+ result = MANAGED_STORAGE_PAGE
+ else:
+ if page > 1: result = page - 1
+ return result
+
+ def get_next_page(self, page):
+ result = page
+ if page == VM_DETAILS_PAGE:
+ install_type = self.__config.get_install_type()
+ if install_type == DomainConfig.LOCAL_INSTALL:
+ result = LOCAL_INSTALL_PAGE
+ elif install_type == DomainConfig.NETWORK_INSTALL:
+ result = NETWORK_INSTALL_PAGE
+ elif install_type == DomainConfig.PXE_INSTALL:
+ result = OS_TYPE_PAGE
+ elif page == LOCAL_INSTALL_PAGE:
+ if self.__config.get_use_cdrom_source():
+ result = SELECT_CDROM_PAGE
+ else:
+ result = SELECT_ISO_PAGE
+ elif page == SELECT_CDROM_PAGE or page == SELECT_ISO_PAGE:
+ result = OS_TYPE_PAGE
+ elif page == NETWORK_INSTALL_PAGE:
+ result = OS_TYPE_PAGE
+ elif page == ENABLE_STORAGE_PAGE:
+ result = BRIDGE_PAGE
+ if self.__config.get_enable_storage():
+ if self.__config.get_use_local_storage():
+ result = LOCAL_STORAGE_PAGE
+ else:
+ result = MANAGED_STORAGE_PAGE
+ elif page == LOCAL_STORAGE_PAGE or page == MANAGED_STORAGE_PAGE:
+ result = BRIDGE_PAGE
+ else:
+ result = page + 1
+ return result
+
+ def page_has_finish(self, page):
+ if page == CONFIRM_PAGE: return True
+ return False
+
+ def page_has_next(self, page):
+ if page < CONFIRM_PAGE:
+ return True
+
+ def get_vm_details_page(self, screen):
+ self.__guest_name = Entry(50, self.__config.get_guest_name())
+ self.__install_type = RadioBar(screen, (("Local install media (ISO image or CDROM)",
+ DomainConfig.LOCAL_INSTALL,
+ self.__config.is_install_type(DomainConfig.LOCAL_INSTALL)),
+ ("Network Install (HTTP, FTP, or NFS)",
+ DomainConfig.NETWORK_INSTALL,
+ self.__config.is_install_type(DomainConfig.NETWORK_INSTALL)),
+ ("Network Boot (PXE)",
+ DomainConfig.PXE_INSTALL,
+ self.__config.is_install_type(DomainConfig.PXE_INSTALL))))
+ grid = Grid(2,3)
+ grid.setField(Label("Name:"), 0, 0, anchorRight = 1)
+ grid.setField(self.__guest_name, 1, 0, anchorLeft = 1)
+ grid.setField(Label("Choose how you would like to install the operating system"), 1, 1,
+ anchorLeft = 1, anchorTop = 1)
+ grid.setField(self.__install_type, 1, 2, anchorLeft = 1)
+ return [Label("Enter your machine details"),
+ grid]
+
+ def get_local_install_page(self, screen):
+ self.__install_source = RadioBar(screen, (("Use CDROM or DVD",
+ DomainConfig.INSTALL_SOURCE_CDROM,
+ self.__config.get_use_cdrom_source()),
+ ("Use ISO image",
+ DomainConfig.INSTALL_SOURCE_ISO,
+ self.__config.get_use_cdrom_source() is False)))
+ grid = Grid(1,1)
+ grid.setField(self.__install_source, 0, 0, anchorLeft = 1)
+ return [Label("Locate your install media"),
+ grid]
+
+ def get_select_cdrom_page(self, screen):
+ drives = []
+ media = self.get_hal().list_installable_volumes()
+ for drive in media.keys():
+ drives.append([media[drive], drive, self.__config.is_install_media(drive)])
+ self.__install_media = RadioBar(screen, (drives))
+ grid = Grid(1, 1)
+ grid.setField(self.__install_media, 0, 0)
+ return [Label("Select the install media"),
+ grid]
+
+ def get_select_iso_page(self, screen):
+ self.__iso_path = Entry(50, self.__config.get_iso_path())
+ grid = Grid(1, 2)
+ grid.setField(Label("Enter ISO path:"), 0, 0, anchorLeft = 1)
+ grid.setField(self.__iso_path, 0, 1, anchorLeft = 1)
+ return [Label("Enter the full path to an install ISO"),
+ grid]
+
+ def get_network_install_page(self, screen):
+ self.__install_url = Entry(50, self.__config.get_install_url())
+ self.__kickstart_url = Entry(50, self.__config.get_kickstart_url())
+ self.__kernel_options = Entry(50, self.__config.get_kernel_options())
+ grid = Grid(2,3)
+ grid.setField(Label("URL:"), 0, 0, anchorRight = 1)
+ grid.setField(self.__install_url, 1, 0, anchorLeft = 1)
+ grid.setField(Label("Kickstart URL:"), 0, 1, anchorRight = 1)
+ grid.setField(self.__kickstart_url, 1, 1, anchorLeft = 1)
+ grid.setField(Label("Kernel Options:"), 0, 2, anchorRight = 1)
+ grid.setField(self.__kernel_options, 1, 2, anchorLeft = 1)
+ return [Label("Provide the operating system URL"),
+ grid]
+
+ def get_os_type_page(self, screen):
+ types = []
+ for type in Guest.list_os_types():
+ types.append([Guest.get_os_type_label(type), type, self.__config.is_os_type(type)])
+ self.__os_types = RadioBar(screen, types)
+ grid = Grid(1, 1)
+ grid.setField(self.__os_types, 0, 0, anchorLeft = 1)
+ return [Label("Choose the operating system type"),
+ grid]
+
+ def get_os_variant_page(self, screen):
+ variants = []
+ type = self.__config.get_os_type()
+ for variant in Guest.list_os_variants(type):
+ variants.append([Guest.get_os_variant_label(type, variant), variant, self.__config.is_os_variant(variant)])
+ self.__os_variants = RadioBar(screen, variants)
+ grid = Grid(1, 1)
+ grid.setField(self.__os_variants, 0, 0, anchorLeft = 1)
+ return [Label("Choose the operating system version"),
+ grid]
+
+ def get_ram_and_cpu_page(self, screen):
+ self.__memory = Entry(10, str(self.__config.get_memory()))
+ self.__cpus = Entry(10, str(self.__config.get_cpus()))
+ grid = Grid(2,2)
+ grid.setField(Label("Memory (RAM):"), 0, 0, anchorRight = 1)
+ grid.setField(self.__memory, 1, 0, anchorLeft = 1)
+ grid.setField(Label("CPUs:"), 0, 1, anchorRight = 1)
+ grid.setField(self.__cpus, 1, 1, anchorLeft = 1)
+ return [Label("Choose memory and CPU settings"),
+ grid]
+
+ def get_enable_storage_page(self, screen):
+ self.__enable_storage = Checkbox("Enable storage for this virtual machine", self.__config.get_enable_storage())
+ self.__storage_type = RadioBar(screen,((["Create a disk image on the computer's hard disk",
+ DomainConfig.NEW_STORAGE,
+ self.__config.get_use_local_storage()]),
+ (["Select managed or other existing storage",
+ DomainConfig.EXISTING_STORAGE,
+ self.__config.get_use_local_storage() is False])))
+ grid = Grid(1,2)
+ grid.setField(self.__enable_storage, 0, 0, anchorLeft = 1)
+ grid.setField(self.__storage_type, 0, 1, anchorLeft = 1)
+ return [Label("Configure storage"),
+ grid]
+
+ def get_local_storage_page(self, screen):
+ self.__storage_size = Entry(6, str(self.__config.get_storage_size()))
+ self.__allocate_storage = Checkbox("Allocate entire disk now", self.__config.get_allocate_storage())
+ grid = Grid(2, 2)
+ grid.setField(self.__allocate_storage, 0, 0, growx = 1, anchorLeft = 1)
+ grid.setField(Label("Storage size (GB):"), 0, 1, anchorLeft = 1)
+ grid.setField(self.__storage_size, 1, 1)
+ return [Label("Configure local storage"),
+ grid]
+
+ def get_managed_storage_page(self, screen):
+ volumes = []
+ for volume in self.get_libvirt().list_storage_volumes():
+ volumes.append(["%s (%d GB)" % (volume.name(), volume.info()[1] / (1024 ** 3)),
+ volume.name(),
+ self.__config.is_existing_storage(volume.name())])
+ self.__existing_storage = RadioBar(screen, (volumes))
+ grid = Grid(2, 1)
+ grid.setField(Label("Existing storage:"), 0, 0)
+ grid.setField(self.__existing_storage, 1, 0)
+ return [Label("Configure managed storage"),
+ grid]
+
+ def get_bridge_page(self, screen):
+ bridges = []
+ for bridge in self.get_libvirt().list_bridges():
+ bridges.append(["Virtual network '%s'" % bridge.name(), bridge.name(), self.__config.get_network_bridge() == bridge.name()])
+ self.__network_bridges = RadioBar(screen, (bridges))
+ if self.__config.get_mac_address() == None:
+ self.__config.set_mac_address(self.get_libvirt().generate_mac_address())
+ self.__mac_address = Entry(20, self.__config.get_mac_address())
+ grid = Grid(1, 1)
+ grid.setField(self.__network_bridges, 0, 0)
+ return [Label("Select an existing bridge"),
+ grid]
+
+ def get_virt_details_page(self, screen):
+ virt_types = []
+ for type in self.get_libvirt().list_virt_types():
+ virt_types.append([type, type, self.__config.is_virt_type(type)])
+ self.__virt_types = RadioBar(screen, (virt_types))
+ archs = []
+ for arch in self.get_libvirt().list_architectures():
+ archs.append([arch, arch, self.__config.is_architecture(arch)])
+ self.__architectures = RadioBar(screen, (archs))
+ grid = Grid(2, 2)
+ grid.setField(Label("Virt Type:"), 0, 0, anchorRight = 1, anchorTop = 1)
+ grid.setField(self.__virt_types, 1, 0, anchorLeft = 1)
+ grid.setField(Label("Architecture:"), 0, 1, anchorRight = 1, anchorTop = 1)
+ grid.setField(self.__architectures, 1, 1, anchorLeft = 1)
+ return [Label("Configure virtualization details"),
+ grid]
+
+ def get_confirm_page(self, screen):
+ grid = Grid(2, 6)
+ grid.setField(Label("OS:"), 0, 0, anchorRight = 1)
+ grid.setField(Label(Guest.get_os_variant_label(self.__config.get_os_type(),
+ self.__config.get_os_variant())), 1, 0, anchorLeft = 1)
+ grid.setField(Label("Install:"), 0, 1, anchorRight = 1)
+ grid.setField(Label(self.__config.get_install_type_text()), 1, 1, anchorLeft = 1)
+ grid.setField(Label("Memory:"), 0, 2, anchorRight = 1)
+ grid.setField(Label("%s MB" % self.__config.get_memory()), 1, 2, anchorLeft = 1)
+ grid.setField(Label("CPUs:"), 0, 3, anchorRight = 1)
+ grid.setField(Label("%d" % self.__config.get_cpus()), 1, 3, anchorLeft = 1)
+ grid.setField(Label("Storage:"), 0, 4, anchorRight = 1)
+ grid.setField(Label(self.__config.get_existing_storage()), 1, 4, anchorLeft = 1)
+ grid.setField(Label("Network:"), 0, 5, anchorRight = 1)
+ grid.setField(Label(self.__config.get_network_bridge()), 1, 5, anchorLeft = 1)
+ return [Label("Ready to begin installation of %s" % self.__config.get_guest_name()),
+ grid]
+
+def DefineDomain():
+ screen = DomainConfigScreen()
+ screen.start()
diff --git a/nodeadmin/destroydomain.py b/nodeadmin/destroydomain.py
new file mode 100755
index 0000000..350c32e
--- /dev/null
+++ b/nodeadmin/destroydomain.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+#
+# destroydomain.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 DestroyDomainConfigScreen(DomainListConfigScreen):
+ LIST_PAGE = 1
+ DESTROY_PAGE = 2
+
+ def __init__(self):
+ DomainListConfigScreen.__init__(self, "Destroy A Domain")
+
+ def get_elements_for_page(self, screen, page):
+ if page is self.LIST_PAGE:
+ return self.get_domain_list_page(screen, defined = False)
+ elif page is self.DESTROY_PAGE:
+ return self.get_destroy_page(screen)
+
+ def page_has_next(self, page):
+ if page is self.LIST_PAGE: return self.has_selectable_domains()
+ return False
+
+ def page_has_back(self, page):
+ if page is self.DESTROY_PAGE: return True
+ return False
+
+ def validate_input(self, page, errors):
+ if page is self.LIST_PAGE:
+ if self.get_selected_domain() is not None:
+ domain = self.get_selected_domain()
+ try:
+ self.get_libvirt().destroy_domain(domain)
+ return True
+ except Exception, error:
+ errors.append("There was an error destroy the domain: %s" % domain)
+ errors.append(str(error))
+ else:
+ errors.append("You must first select a domain to destroy.")
+ return False
+
+ def get_destroy_page(self, screen):
+ grid = Grid(1, 1)
+ grid.setField(Label("%s was successfully destroyed." % self.get_selected_domain()), 0, 0)
+ return [grid]
+
+def DestroyDomain():
+ screen = DestroyDomainConfigScreen()
+ screen.start()
diff --git a/nodeadmin/domainconfig.py b/nodeadmin/domainconfig.py
new file mode 100644
index 0000000..ef39fe0
--- /dev/null
+++ b/nodeadmin/domainconfig.py
@@ -0,0 +1,217 @@
+# domainconfig.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 virtinst import Guest
+
+class DomainConfig:
+ LOCAL_INSTALL = "local"
+ NETWORK_INSTALL = "network"
+ PXE_INSTALL = "pxe"
+ INSTALL_TYPE_TEXT = {LOCAL_INSTALL : "Local CDROM/ISO",
+ NETWORK_INSTALL : "URL INstall Tree",
+ PXE_INSTALL : "PXE Install"}
+
+ INSTALL_SOURCE_CDROM = "cdrom"
+ INSTALL_SOURCE_ISO = "iso"
+
+ NEW_STORAGE = "new"
+ EXISTING_STORAGE = "existing"
+
+ def __init__(self):
+ self.__guest_name = ""
+ self.__install_type = DomainConfig.LOCAL_INSTALL
+ self.__use_cdrom_source = True
+ self.__install_location = ""
+ self.__install_media = ""
+ self.__iso_path = ""
+ self.__install_url = ""
+ self.__kickstart_url = ""
+ self.__kernel_options = ""
+ self.__os_type = "other"
+ self.__os_variant = None
+ self.__memory = 512
+ self.__cpus = 1
+ self.__enable_storage = True
+ self.__use_local_storage = True
+ self.__storage_size = 8.0
+ self.__allocate_storage = True
+ self.__existing_storage = ""
+ self.__network_bridge = None
+ self.__mac_address = None
+ self.__virt_type = None
+ self.__architecture = None
+
+ def set_guest_name(self, name):
+ self.__guest_name = name
+
+ def get_guest_name(self):
+ return self.__guest_name
+
+ def set_install_type(self, type):
+ self.__install_type = type
+
+ def get_install_type(self):
+ return self.__install_type
+
+ def get_install_type_text(self):
+ return DomainConfig.INSTALL_TYPE_TEXT[self.get_install_type()]
+
+ def is_install_type(self, type):
+ return self.__install_type == type
+
+ def set_install_location(self, location):
+ self.__install_location = location
+
+ def set_use_cdrom_source(self, use):
+ self.__use_cdrom_source = use
+
+ def get_use_cdrom_source(self):
+ return self.__use_cdrom_source
+
+ def get_install_location(self):
+ return self.__install_location
+
+ def is_install_location(self, location):
+ return self.__install_location == location
+
+ def set_install_media(self, media):
+ self.__install_media = media
+
+ def get_install_media(self):
+ return self.__install_media
+
+ def is_install_media(self, media):
+ return self.__install_media == media
+
+ def set_iso_path(self, path):
+ self.__iso_path = path
+
+ def get_iso_path(self):
+ return self.__iso_path
+
+ def set_install_url(self, url):
+ self.__install_url = url
+
+ def get_install_url(self):
+ return self.__install_url
+
+ def set_kickstart_url(self, url):
+ self.__kickstart_url = url
+
+ def get_kickstart_url(self):
+ return self.__kickstart_url
+
+ def set_kernel_options(self, options):
+ self.__kernel_options = options
+
+ def get_kernel_options(self):
+ return self.__kernel_options
+
+ def set_os_type(self, type):
+ self.__os_type = type
+ self.__os_variant = Guest.list_os_variants(type)[0]
+
+ def get_os_type(self):
+ return self.__os_type
+
+ def is_os_type(self, type):
+ return self.__os_type == type
+
+ def set_os_variant(self, variant):
+ self.__os_variant = variant
+
+ def get_os_variant(self):
+ return self.__os_variant
+
+ def is_os_variant(self, variant):
+ return self.__os_variant == variant
+
+ def set_memory(self, memory):
+ self.__memory = int(memory)
+
+ def get_memory(self):
+ return self.__memory
+
+ def set_cpus(self, cpus):
+ self.__cpus = cpus
+
+ def get_cpus(self):
+ return self.__cpus
+
+ def set_enable_storage(self, enable):
+ self.__enable_storage = enable
+
+ def get_enable_storage(self):
+ return self.__enable_storage
+
+ def set_use_local_storage(self, use):
+ self.__use_local_storage = use
+
+ def get_use_local_storage(self):
+ return self.__use_local_storage
+
+ def set_storage_size(self, size):
+ self.__storage_size = size
+
+ def get_storage_size(self):
+ return self.__storage_size
+
+ def set_allocate_storage(self, allocate):
+ self.__allocate_storage = allocate
+
+ def get_allocate_storage(self):
+ return self.__allocate_storage
+
+ def set_existing_storage(self, storage):
+ self.__existing_storage = storage
+
+ def get_existing_storage(self):
+ return self.__existing_storage
+
+ def is_existing_storage(self, storage):
+ return self.__existing_storage == storage
+
+ def set_network_bridge(self, bridge):
+ self.__network_bridge = bridge
+
+ def get_network_bridge(self):
+ return self.__network_bridge
+
+ def set_mac_address(self, address):
+ self.__mac_address = address
+
+ def get_mac_address(self):
+ return self.__mac_address
+
+ def set_virt_type(self, type):
+ self.__virt_type = type
+
+ def get_virt_type(self):
+ return self.__virt_type
+
+ def is_virt_type(self, type):
+ return self.__virt_type == type
+
+ def set_architecture(self, architecture):
+ self.__architecture = architecture
+
+ def get_architecture(self):
+ return self.__architecture
+
+ def is_architecture(self, architecture):
+ return self.__architecture == architecture
diff --git a/nodeadmin/halworker.py b/nodeadmin/halworker.py
new file mode 100644
index 0000000..448c22d
--- /dev/null
+++ b/nodeadmin/halworker.py
@@ -0,0 +1,37 @@
+# halworker.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.
+
+import dbus
+import virtinst
+
+class HALWorker:
+ '''Provides utilities for working with HAL to get hardware information.'''
+ def __init__(self):
+ self.__bus = dbus.SystemBus()
+ hobj = self.__bus.get_object("org.freedesktop.Hal", "/org/freedesktop/Hal/Manager")
+ self.__conn = dbus.Interface(hobj, "org.freedesktop.Hal.Manager")
+
+ def list_installable_volumes(self):
+ result = {}
+ for udi in self.__conn.FindDeviceByCapability("volume"):
+ device = self.__bus.get_object("org.freedesktop.Hal", udi)
+ info = dbus.Interface(device, "org.freedesktop.Hal.Device")
+ if info.GetProperty("volume.is_disc"):
+ if info.GetProperty("volume.disc.has_data"):
+ result[str(info.GetProperty("block.device"))] = info.GetProperty("volume.label")
+ return result
diff --git a/nodeadmin/libvirtworker.py b/nodeadmin/libvirtworker.py
new file mode 100644
index 0000000..adaea16
--- /dev/null
+++ b/nodeadmin/libvirtworker.py
@@ -0,0 +1,276 @@
+# libvirtworker.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.
+
+import dbus
+import libvirt
+import os
+import virtinst
+import utils
+
+from domainconfig import DomainConfig
+
+DEFAULT_POOL_TARGET_PATH="/var/lib/libvirt/images"
+
+class LibvirtWorker:
+ '''Provides utilities for interfacing with libvirt.'''
+ def __init__(self, url = "qemu:///system"):
+ self.__conn = libvirt.open(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 list_domains(self, defined = True, started = True):
+ '''Lists all domains.'''
+ result = []
+ if defined:
+ result.extend(self.__conn.listDefinedDomains())
+ if started:
+ for id in self.__conn.listDomainsID():
+ result.append(self.__conn.lookupByID(id).name())
+ return result
+
+ def get_domain(self, name):
+ '''Returns the specified domain.'''
+ result = self.__conn.lookupByName(name)
+ if result is None: raise Exception("No such domain exists: %s" % name)
+
+ return result
+
+ def domain_exists(self, name):
+ '''Returns whether a domain with the specified node exists.'''
+ domains = self.list_domains()
+ if name in domains: return True
+ return False
+
+ def create_domain(self, name):
+ '''Creates the specified domain.'''
+ domain = self.get_domain(name)
+ domain.create()
+
+ def destroy_domain(self, name):
+ '''Destroys the specified domain.'''
+ domain = self.get_domain(name)
+ domain.destroy()
+
+ def undefine_domain(self, name):
+ '''Undefines the specified domain.'''
+ domain = self.get_domain(name)
+ domain.undefine()
+
+ def list_storage_pools(self):
+ '''Returns the list of all defined storage pools.'''
+ return self.__conn.listStoragePools()
+
+ def storage_pool_exists(self, name):
+ '''Returns whether a storage pool exists.'''
+ pools = self.list_storage_pools()
+ if name in pools: return True
+ return False
+
+ def define_storage_pool(self, name):
+ '''Defines a storage pool with the given name.'''
+ try:
+ pool = virtinst.Storage.DirectoryPool(conn=self.__conn,
+ name=name,
+ target_path=DEFAULT_POOL_TARGET_PATH)
+ newpool = pool.install(build=True, create=True)
+ newpool.setAutostart(True)
+ except Exception, error:
+ raise RuntimeError("Could not create pool: %s - %s", str(error))
+
+ def list_bridges(self):
+ '''Lists all defined and active bridges.'''
+ bridges = self.__conn.listNetworks()
+ bridges.extend(self.__conn.listDefinedNetworks())
+ result = []
+ for name in bridges:
+ bridge = self.__conn.networkLookupByName(name)
+ result.append(bridge)
+ return result
+
+ def generate_mac_address(self):
+ return self.__net.macaddr
+
+ def list_storage_volumes(self):
+ '''Lists all defined storage volumes.'''
+ pools = self.__conn.listStoragePools()
+ pools.extend(self.__conn.listDefinedStoragePools())
+ result = []
+ for name in pools:
+ pool = self.__conn.storagePoolLookupByName(name)
+ for volname in pool.listVolumes():
+ volume = self.__conn.storageVolLookupByPath("/var/lib/libvirt/images/%s" % volname)
+ result.append(volume)
+ return result
+
+ def get_storage_size(self, name):
+ '''Returns the size of the specified storage volume.'''
+ volume = self.__conn.storageVolLookupByPath("/var/lib/libvirt/images/%s" % name)
+ return volume.info()[1] / (1024.0 ** 3)
+
+ def get_virt_types(self):
+ result = []
+ for guest in self.__capabilities.guests:
+ guest_type = guest.os_type
+ for domain in guest.domains:
+ domain_type = domain.hypervisor_type
+ label = domain_type
+
+ if domain_type is "kvm" and guest_type is "xen": label = "xenner"
+ elif domain_type is "xen":
+ if guest_type is "xen":
+ label = "xen (paravirt)"
+ elif guest_type is "kvm":
+ label = "xen (fullvirt)"
+ elif domain_type is "test":
+ if guest_type is "xen":
+ label = "test (xen)"
+ elif guest_type is "hvm":
+ label = "test (hvm)"
+
+ for row in result:
+ if row[0] == label:
+ label = None
+ break
+ if label is None: continue
+
+ result.append([label, domain_type, guest_type])
+ return result
+
+ def list_virt_types(self):
+ virt_types = self.get_virt_types()
+ result = []
+ for type in virt_types:
+ result.append(type[0])
+ return result
+
+ def get_default_architecture(self):
+ '''Returns a default hypervisor type for new domains.'''
+ return self.__new_guest.arch
+
+ def get_hypervisor(self, virt_type):
+ virt_types = self.get_virt_types()
+ for type in virt_types:
+ if type[0] is virt_type: return type[1]
+ return None
+
+ def get_default_virt_type(self):
+ '''Returns the default virtualization type for new domains.'''
+ return self.__new_domain.hypervisor_type
+
+ def get_os_type(self, virt_type):
+ virt_types = self.get_virt_types()
+ for type in virt_types:
+ if type[0] is virt_type: return type[2]
+ return None
+
+ def list_architectures(self):
+ result = []
+ for guest in self.__capabilities.guests:
+ for domain in guest.domains:
+ label = guest.arch
+ for row in result:
+ if row == label:
+ label = None
+ break
+ if label is None: continue
+
+ result.append(label)
+ return result
+
+ def define_domain(self, config, meter):
+ location = extra = kickstart = None
+
+ if config.get_install_type() == DomainConfig.LOCAL_INSTALL:
+ if config.get_use_cdrom_source():
+ iclass = virtinst.DistroInstaller
+ location = config.get_install_media()
+ else:
+ iclass = virtinst.LiveCDInstaller
+ location = config.get_iso_path()
+ elif config.get_install_type() == DomainConfig.NETWORK_INSTALL:
+ iclass = virtinst.DistroInstaller
+ location = config.get_install_url()
+ extra = config.get_kernel_options()
+ kickstart = config.get_kickstart_url()
+ elif config.get_install_type() == DomainConfig.PXE_INSTALL:
+ iclass = virtinst.PXEInstaller
+
+ installer = iclass(conn = self.__conn,
+ type = self.get_hypervisor(config.get_virt_type()),
+ os_type = self.get_os_type(config.get_virt_type()))
+ self.__guest = installer.guest_from_installer()
+ self.__guest.name = config.get_guest_name()
+ self.__guest.vcpus = config.get_cpus()
+ self.__guest.memory = config.get_memory()
+ self.__guest.maxmemory = config.get_memory()
+
+ self.__guest.installer.location = location
+ if config.get_use_cdrom_source(): self.__guest.installer.cdrom = True
+ extraargs = ""
+ if extra: extraargs += extra
+ if kickstart: extraargs += " ks=%s" % kickstart
+ if extraargs: self.__guest.installer.extraarags = extraargs
+
+ self.__guest.uuid = virtinst.util.uuidToString(virtinst.util.randomUUID())
+
+ if config.get_os_type() != "generic": self.__guest.os_type = config.get_os_type()
+ if config.get_os_variant() != "generic": self.__guest.os_variant = config.get_os_variant()
+
+ self.__guest._graphics_dev = virtinst.VirtualGraphics(type = virtinst.VirtualGraphics.TYPE_VNC)
+ self.__guest.sound_devs = []
+ self.__guest.sound_devs.append(virtinst.VirtualAudio(model = "es1370"))
+
+ self._setup_nics(config)
+ self._setup_disks(config)
+
+ self.__guest.conn = self.__conn
+ self.__domain = self.__guest.start_install(False, meter = meter)
+
+ def _setup_nics(self, config):
+ self.__guest.nics = []
+ nic = virtinst.VirtualNetworkInterface(type = virtinst.VirtualNetworkInterface.TYPE_VIRTUAL,
+ bridge = config.get_network_bridge(),
+ network = config.get_network_bridge(),
+ macaddr = config.get_mac_address())
+ self.__guest.nics.append(nic)
+ # ensure the network is running
+ if config.get_network_bridge() not in self.__conn.listNetworks():
+ network = self.__conn.networkLookupByName(config.get_network_bridge())
+ network.create()
+
+ def _setup_disks(self, config):
+ self.__guest.disks = []
+ if config.get_enable_storage():
+ path = None
+ if config.get_use_local_storage():
+ if self.storage_pool_exists("default") is False:
+ self.define_storage_pool("default")
+ pool = self.__conn.storagePoolLookupByName("default")
+ path = virtinst.Storage.StorageVolume.find_free_name(config.get_guest_name(),
+ pool_object = pool,
+ suffix = ".img")
+ path = os.path.join(DEFAULT_POOL_TARGET_PATH, path)
+
+ if path is not None:
+ storage= virtinst.VirtualDisk(conn = self.__conn,
+ path = path,
+ size = config.get_storage_size())
+ self.__guest.disks.append(storage)
+ self.__guest.conn = self.__conn
diff --git a/nodeadmin/listdomains.py b/nodeadmin/listdomains.py
new file mode 100755
index 0000000..1b51ee2
--- /dev/null
+++ b/nodeadmin/listdomains.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+#
+# listdomains.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 *
+
+class ListDomainsConfigScreen(DomainListConfigScreen):
+ LIST_PAGE = 1
+ DETAIL_PAGE = 2
+
+ def __init__(self):
+ DomainListConfigScreen.__init__(self, 'List Domains')
+
+ def page_has_next(self, page):
+ return (page == self.LIST_PAGE)
+
+ def page_has_back(self, page):
+ return (page == self.DETAIL_PAGE)
+
+ def validate_input(self, page, errors):
+ if page == self.LIST_PAGE:
+ if self.get_selected_domain() is None:
+ errors.append("Please select a domain to view.")
+ else:
+ return True
+
+ def get_elements_for_page(self, screen, page):
+ if page == self.LIST_PAGE:
+ return self.get_domain_list_page(screen)
+ elif page == self.DETAIL_PAGE:
+ return self.get_detail_page_elements(screen)
+
+ def get_detail_page_elements(self, screen):
+ domain = self.get_libvirt().get_domain(self.get_selected_domain())
+ grid = Grid(2, 5)
+ grid.setField(Label("Name: "), 0, 0, anchorRight = 1)
+ grid.setField(Label(domain.name()), 1, 0, anchorLeft = 1)
+ grid.setField(Label("UUID: "), 0, 1, anchorRight = 1)
+ grid.setField(Label(domain.UUIDString()), 1, 1, anchorLeft = 1)
+ grid.setField(Label("OS Type: "), 0, 2, anchorRight = 1)
+ grid.setField(Label(domain.OSType()), 1, 2, anchorLeft = 1)
+ grid.setField(Label("Max. Memory: "), 0, 3, anchorRight = 1)
+ grid.setField(Label(str(domain.maxMemory())), 1, 3, anchorLeft = 1)
+ grid.setField(Label("Max. VCPUs: "), 0, 4, anchorRight = 1)
+ grid.setField(Label(str(domain.maxVcpus())), 1, 4, anchorLeft = 1)
+ return [grid]
+
+def ListDomains():
+ screen = ListDomainsConfigScreen()
+ screen.start()
diff --git a/nodeadmin/mainmenu.py b/nodeadmin/mainmenu.py
new file mode 100755
index 0000000..497ad57
--- /dev/null
+++ b/nodeadmin/mainmenu.py
@@ -0,0 +1,74 @@
+# mainmenu.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 traceback
+from configscreen import ConfigScreen
+from definedomain import DefineDomain
+from createdomain import CreateDomain
+from destroydomain import DestroyDomain
+from undefinedomain import UndefineDomain
+from listdomains import ListDomains
+from createuser import CreateUser
+import utils
+import logging
+
+DEFINE_DOMAIN = 1
+CREATE_DOMAIN = 2
+DESTROY_DOMAIN = 3
+UNDEFINE_DOMAIN = 4
+LIST_DOMAINS = 5
+CREATE_USER = 6
+EXIT_CONSOLE = 99
+
+def MainMenu():
+ finished = False
+ while finished == False:
+ screen = SnackScreen()
+ menu = Listbox(height = 0, width = 0, returnExit = 1)
+ menu.append("Define A Domain", DEFINE_DOMAIN)
+ menu.append("Create A Domain", CREATE_DOMAIN)
+ menu.append("Destroy A Domain", DESTROY_DOMAIN)
+ menu.append("Undefine A Domain", UNDEFINE_DOMAIN)
+ menu.append("List All Domains", LIST_DOMAINS)
+ menu.append("Create A User", CREATE_USER)
+ menu.append("Exit Administration", EXIT_CONSOLE)
+ gridform = GridForm(screen, "Node Administration Console", 1, 4)
+ gridform.add(menu, 0, 0)
+ result = gridform.run();
+ screen.popWindow()
+ screen.finish()
+
+ try:
+ if result.current() == DEFINE_DOMAIN: DefineDomain()
+ elif result.current() == CREATE_DOMAIN: CreateDomain()
+ elif result.current() == DESTROY_DOMAIN: DestroyDomain()
+ elif result.current() == UNDEFINE_DOMAIN: UndefineDomain()
+ elif result.current() == LIST_DOMAINS: ListDomains()
+ elif result.current() == CREATE_USER: CreateUser()
+ elif result.current() == EXIT_CONSOLE: finished = True
+ except Exception, error:
+ screen = SnackScreen()
+ logging.info("An exception occurred: %s" % str(error))
+ ButtonChoiceWindow(screen,
+ "An Exception Has Occurred",
+ str(error) + "\n" + traceback.format_exc(),
+ buttons = ["OK"])
+ screen.popWindow()
+ screen.finish()
+ finished = True
diff --git a/nodeadmin/nodeadmin.py b/nodeadmin/nodeadmin.py
new file mode 100755
index 0000000..864a4c0
--- /dev/null
+++ b/nodeadmin/nodeadmin.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+#
+# node-admin - 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.
+
+import sys
+
+from mainmenu import MainMenu
+
+def NodeAdmin():
+ MainMenu()
+
+if __name__ == "__main__":
+ sys.exit(NodeAdmin())
diff --git a/nodeadmin/setup.py.in b/nodeadmin/setup.py.in
new file mode 100644
index 0000000..f51a34c
--- /dev/null
+++ b/nodeadmin/setup.py.in
@@ -0,0 +1,34 @@
+# setup.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 setuptools import setup, find_packages
+
+setup(name = "nodeadmin",
+ version = "@VERSION@",
+ package_dir = {'nodeadmin': 'nodeadmin'},
+ packages = find_packages('.'),
+ entry_points = {
+ 'console_scripts': [
+ 'nodeadmin = nodeadmin.nodeadmin:NodeAdmin',
+ 'definedom = nodeadmin.definedomain:DefineDomain',
+ 'createdom = nodeadmin.createdomain:CreateDomain',
+ 'destroydom = nodeadmin.destroydomain:DestroyDomain',
+ 'undefinedom = nodeadmin.undefinedomain:UndefineDomain',
+ 'createuser = nodeadmin.createuser:CreateUser',
+ 'listdoms = nodeadmin.listdomains:ListDomains']
+ })
diff --git a/nodeadmin/undefinedomain.py b/nodeadmin/undefinedomain.py
new file mode 100755
index 0000000..2620540
--- /dev/null
+++ b/nodeadmin/undefinedomain.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+#
+# undefinedomain.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 UndefineDomainConfigScreen(DomainListConfigScreen):
+ LIST_PAGE = 1
+ CONFIRM_PAGE = 2
+ UNDEFINE_PAGE = 3
+
+ def __init__(self):
+ DomainListConfigScreen.__init__(self, "Undefine A Domain")
+
+ def get_elements_for_page(self, screen, page):
+ if page is self.LIST_PAGE: return self.get_domain_list_page(screen)
+ elif page is self.CONFIRM_PAGE: return self.get_confirm_page(screen)
+ elif page is self.UNDEFINE_PAGE: return self.get_undefine_page(screen)
+
+ def page_has_next(self, page):
+ if page is self.LIST_PAGE: return self.has_selectable_domains()
+ elif page is self.CONFIRM_PAGE: return True
+ return False
+
+ def page_has_back(self, page):
+ if page is self.CONFIRM_PAGE: return True
+ elif page is self.UNDEFINE_PAGE: return True
+ return False
+
+ def get_back_page(self, page):
+ if page is self.CONFIRM_PAGE: return self.LIST_PAGE
+ elif page is self.UNDEFINE_PAGE: return self.LIST_PAGE
+
+ def validate_input(self, page, errors):
+ if page is self.LIST_PAGE:
+ if self.get_selected_domain() is not None:
+ return True
+ else:
+ errors.append("You must first select a domain.")
+ elif page is self.CONFIRM_PAGE:
+ if self.__confirm_undefine.value():
+ domain = self.get_selected_domain()
+ try:
+ self.get_libvirt().undefine_domain(domain)
+ return True
+ except Exception, error:
+ errors.append("Failed to undefine %s." % domain)
+ errors.append(str(error))
+ else:
+ errors.append("You must confirm undefining the domain to proceed.")
+ return False
+
+ def get_confirm_page(self, screen):
+ self.__confirm_undefine = Checkbox("Check here to confirm undefining %s." % self.get_selected_domain(), 0)
+ grid = Grid(1, 1)
+ grid.setField(self.__confirm_undefine, 0, 0)
+ return [grid]
+
+ def get_undefine_page(self, screen):
+ grid = Grid(1, 1)
+ grid.setField(Label("%s has been undefined." % self.get_selected_domain()), 0, 0)
+ return [grid]
+
+def UndefineDomain():
+ screen = UndefineDomainConfigScreen()
+ screen.start()
diff --git a/nodeadmin/userworker.py b/nodeadmin/userworker.py
new file mode 100644
index 0000000..167197b
--- /dev/null
+++ b/nodeadmin/userworker.py
@@ -0,0 +1,38 @@
+# userworker.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.
+
+import libuser
+
+class UserWorker:
+ '''Provides APIs for creating, modifying and deleting user accounts.'''
+ def __init__(self):
+ self.__admin = libuser.admin()
+
+ def create_user(self, username, password, other_group):
+ '''Creates a new user account with the provides username,
+ password. The user is also added to the optional group
+ if one is specified.'''
+ user = self.__admin.initUser(username)
+ user.set('pw_passwd', password)
+ self.__admin.addUser(user)
+ if other_group is not None:
+ group = self.__admin.lookupGroupByName(other_group)
+ if group is None: raise Exception("Invalid group specified: %s" % other_group)
+ user.add('pw_gid', group.get('pw_gid')[0])
+ self.__admin.modifyUser(user)
+
diff --git a/ovirt-node.spec.in b/ovirt-node.spec.in
index 12815c9..ee1942b 100644
--- a/ovirt-node.spec.in
+++ b/ovirt-node.spec.in
@@ -1,5 +1,7 @@
%define product_family oVirt Node
%define beta Beta
+%{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
+
Summary: The oVirt Node daemons/scripts
Name: ovirt-node
@@ -21,6 +23,8 @@ Requires(post): /sbin/chkconfig
Requires(preun): /sbin/chkconfig
BuildRequires: libvirt-devel >= 0.5.1
BuildRequires: dbus-devel hal-devel
+BuildRequires: python-devel
+BuildRequires: python-setuptools
Requires: libvirt >= 0.6.3
Requires: augeas >= 0.3.5
Requires: libvirt-qpid >= 0.2.14-3
@@ -44,6 +48,10 @@ Requires: nc
Requires: grub
Requires: /usr/sbin/crond
Requires: anyterm
+Requires: newt-python
+Requires: libuser-python
+Requires: dbus-python
+
ExclusiveArch: %{ix86} x86_64
%define app_root %{_datadir}/%{name}
@@ -144,6 +152,7 @@ cd -
%{__install} -d -m0755 %{buildroot}%{_sysconfdir}/cron.d
%{__install} -d -m0755 %{buildroot}%{_sysconfdir}/cron.hourly
%{__install} -d -m0755 %{buildroot}%{_sysconfdir}/logrotate.d
+%{__install} -d -m0755 %{buildroot}%{python_sitelib}/nodeadmin
%{__install} -p -m0755 scripts/ovirt-awake %{buildroot}%{_sbindir}
%{__install} -p -m0755 scripts/ovirt-config-boot %{buildroot}%{_sbindir}
@@ -164,6 +173,22 @@ cd -
%{__install} -p -m0755 scripts/persist %{buildroot}%{_sbindir}
%{__install} -p -m0755 scripts/unpersist %{buildroot}%{_sbindir}
+%{__install} -p -m0644 nodeadmin/__init__.py %{buildroot}%{python_sitelib}/nodeadmin
+%{__install} -p -m0644 nodeadmin/configscreen.py %{buildroot}%{python_sitelib}/nodeadmin
+%{__install} -p -m0755 nodeadmin/createdomain.py %{buildroot}%{python_sitelib}/nodeadmin
+%{__install} -p -m0755 nodeadmin/createuser.py %{buildroot}%{python_sitelib}/nodeadmin
+%{__install} -p -m0755 nodeadmin/definedomain.py %{buildroot}%{python_sitelib}/nodeadmin
+%{__install} -p -m0755 nodeadmin/destroydomain.py %{buildroot}%{python_sitelib}/nodeadmin
+%{__install} -p -m0644 nodeadmin/domainconfig.py %{buildroot}%{python_sitelib}/nodeadmin
+%{__install} -p -m0644 nodeadmin/halworker.py %{buildroot}%{python_sitelib}/nodeadmin
+%{__install} -p -m0644 nodeadmin/libvirtworker.py %{buildroot}%{python_sitelib}/nodeadmin
+%{__install} -p -m0644 nodeadmin/userworker.py %{buildroot}%{python_sitelib}/nodeadmin
+%{__install} -p -m0755 nodeadmin/listdomains.py %{buildroot}%{python_sitelib}/nodeadmin
+%{__install} -p -m0644 nodeadmin/mainmenu.py %{buildroot}%{python_sitelib}/nodeadmin
+%{__install} -p -m0755 nodeadmin/nodeadmin.py %{buildroot}%{python_sitelib}/nodeadmin
+%{__install} -p -m0755 nodeadmin/undefinedomain.py %{buildroot}%{python_sitelib}/nodeadmin
+%{__install} -p -m0755 nodeadmin/utils.py %{buildroot}%{python_sitelib}/nodeadmin
+
# gptsync
%{__install} -p -m0755 gptsync/gptsync %{buildroot}%{_sbindir}
%{__install} -p -m0755 gptsync/showpart %{buildroot}%{_sbindir}
@@ -182,6 +207,10 @@ cd -
%{__install} -p -m0644 logrotate/ovirt-logrotate %{buildroot}%{_sysconfdir}/cron.d
%{__install} -p -m0644 logrotate/ovirt-logrotate.conf %{buildroot}%{_sysconfdir}/logrotate.d
+# install the admin tools
+python nodeadmin/setup.py install --root %{buildroot}
+# rm -rf %{buildroot}%{python_sitelib}/nodeadmin- at VERSION@*
+
echo "oVirt Node release %{version}-%{release}" > %{buildroot}%{_sysconfdir}/ovirt-release
mkdir -p %{buildroot}/%{_sysconfdir}/default
touch %{buildroot}/%{_sysconfdir}/default/ovirt
@@ -325,7 +354,16 @@ fi
%{_sbindir}/ovirt-awake
%{_initrddir}/ovirt-functions
%defattr(-,root,root,0644)
+%{_bindir}/nodeadmin
+%{_bindir}/definedom
+%{_bindir}/createdom
+%{_bindir}/destroydom
+%{_bindir}/undefinedom
+%{_bindir}/listdoms
+%{_bindir}/createuser
%{_sysconfdir}/collectd.conf.in
+%{python_sitelib}/nodeadmin
+%{python_sitelib}/nodeadmin- at VERSION@-py2.6.egg-info
%config %attr(0644,root,root) %{_sysconfdir}/ovirt-release
%config %attr(0644,root,root) %{_sysconfdir}/default/ovirt
--
1.6.2.5
More information about the ovirt-devel
mailing list