[Ovirt-devel] [PATCH node] Introduces the node-admin toolset.

Joey Boggs jboggs at redhat.com
Mon Aug 31 20:48:41 UTC 2009


Darryl L. Pierce wrote:
> 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             |   14 ++
>  admin/__init__.py       |   20 ++
>  admin/configscreen.py   |  150 +++++++++++++++
>  admin/createdomain.py   |   68 +++++++
>  admin/createuser.py     |  106 +++++++++++
>  admin/definedomain.py   |  470 +++++++++++++++++++++++++++++++++++++++++++++++
>  admin/destroydomain.py  |   66 +++++++
>  admin/domainconfig.py   |  217 ++++++++++++++++++++++
>  admin/halworker.py      |   37 ++++
>  admin/libvirtworker.py  |  248 +++++++++++++++++++++++++
>  admin/listdomains.py    |   68 +++++++
>  admin/mainmenu.py       |   71 +++++++
>  admin/nodeadmin.py      |   29 +++
>  admin/setup.py.in       |   34 ++++
>  admin/undefinedomain.py |   83 +++++++++
>  admin/userworker.py     |   38 ++++
>  configure.ac            |    1 +
>  ovirt-node.spec.in      |   36 ++++
>  19 files changed, 1757 insertions(+), 0 deletions(-)
>  create mode 100644 admin/__init__.py
>  create mode 100644 admin/configscreen.py
>  create mode 100755 admin/createdomain.py
>  create mode 100755 admin/createuser.py
>  create mode 100755 admin/definedomain.py
>  create mode 100755 admin/destroydomain.py
>  create mode 100644 admin/domainconfig.py
>  create mode 100644 admin/halworker.py
>  create mode 100644 admin/libvirtworker.py
>  create mode 100755 admin/listdomains.py
>  create mode 100755 admin/mainmenu.py
>  create mode 100755 admin/nodeadmin.py
>  create mode 100644 admin/setup.py.in
>  create mode 100755 admin/undefinedomain.py
>  create mode 100644 admin/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..5cb0c67 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -26,6 +26,20 @@ EXTRA_DIST =			\
>    ovirt-node-selinux.fc		\
>    images/grub-splash.xpm.gz	\
>    images/syslinux-vesa-splash.jpg	\
> +  admin/configscreen.py         \
> +  admin/createuser.py           \
> +  admin/destroydomain.py        \
> +  admin/halworker.py            \
> +  admin/libvirtworker.py        \
> +  admin/userworker.py           \
> +  admin/mainmenu.py             \
> +  admin/undefinedomain.py       \
> +  admin/createdomain.py         \
> +  admin/definedomain.py         \
> +  admin/domainconfig.py         \
> +  admin/listdomains.py          \
> +  admin/nodeadmin.py            \
> +  admin/setup.py                \
>    scripts/collectd.conf.in	\
>    scripts/ovirt			\
>    scripts/ovirt-awake		\
> diff --git a/admin/__init__.py b/admin/__init__.py
> new file mode 100644
> index 0000000..1f3c72c
> --- /dev/null
> +++ b/admin/__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/admin/configscreen.py b/admin/configscreen.py
> new file mode 100644
> index 0000000..0282eee
> --- /dev/null
> +++ b/admin/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/admin/createdomain.py b/admin/createdomain.py
> new file mode 100755
> index 0000000..b73a09e
> --- /dev/null
> +++ b/admin/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/admin/createuser.py b/admin/createuser.py
> new file mode 100755
> index 0000000..dbc4626
> --- /dev/null
> +++ b/admin/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/admin/definedomain.py b/admin/definedomain.py
> new file mode 100755
> index 0000000..ea4c986
> --- /dev/null
> +++ b/admin/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
> +
> +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):
> +        print "Starting..."
> +
> +    def _do_end(self, amount_read, now = None):
> +        print "Ending: read=%d" % amount_read
> +
> +    def _do_update(self, amount_read, now = None):
> +        print "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/admin/destroydomain.py b/admin/destroydomain.py
> new file mode 100755
> index 0000000..350c32e
> --- /dev/null
> +++ b/admin/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/admin/domainconfig.py b/admin/domainconfig.py
> new file mode 100644
> index 0000000..ef39fe0
> --- /dev/null
> +++ b/admin/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/admin/halworker.py b/admin/halworker.py
> new file mode 100644
> index 0000000..448c22d
> --- /dev/null
> +++ b/admin/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/admin/libvirtworker.py b/admin/libvirtworker.py
> new file mode 100644
> index 0000000..054d09e
> --- /dev/null
> +++ b/admin/libvirtworker.py
> @@ -0,0 +1,248 @@
> +# 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
> +
> +from domainconfig import DomainConfig
> +
> +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_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_is_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)
> +
> +    def _setup_disks(self, config):
> +        self.__guest.disks = []
> +        if config.get_enable_storage():
> +            path = None
> +            if config.get_use_local_storage():
> +                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("/var/lib/libvirt/images/", 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/admin/listdomains.py b/admin/listdomains.py
> new file mode 100755
> index 0000000..1b51ee2
> --- /dev/null
> +++ b/admin/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/admin/mainmenu.py b/admin/mainmenu.py
> new file mode 100755
> index 0000000..2808dad
> --- /dev/null
> +++ b/admin/mainmenu.py
> @@ -0,0 +1,71 @@
> +# 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
> +
> +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()
> +            ButtonChoiceWindow(screen,
> +                               "An Exception Has Occurred",
> +                               str(error) + "\n" + traceback.format_exc(),
> +                               buttons = ["OK"])
> +            screen.popWindow()
> +            screen.finish()
> +            finished = True
> diff --git a/admin/nodeadmin.py b/admin/nodeadmin.py
> new file mode 100755
> index 0000000..864a4c0
> --- /dev/null
> +++ b/admin/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/admin/setup.py.in b/admin/setup.py.in
> new file mode 100644
> index 0000000..ac9981a
> --- /dev/null
> +++ b/admin/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': 'admin'},
> +      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/admin/undefinedomain.py b/admin/undefinedomain.py
> new file mode 100755
> index 0000000..2620540
> --- /dev/null
> +++ b/admin/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/admin/userworker.py b/admin/userworker.py
> new file mode 100644
> index 0000000..167197b
> --- /dev/null
> +++ b/admin/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/configure.ac b/configure.ac
> index d965a82..e778b10 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
> +  admin/setup.py
>    gptsync/Makefile
>    ovirt-node.spec
>    ])
> diff --git a/ovirt-node.spec.in b/ovirt-node.spec.in
> index 12815c9..f9649ed 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:  setuptool
>  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,21 @@ cd -
>  %{__install} -p -m0755 scripts/persist %{buildroot}%{_sbindir}
>  %{__install} -p -m0755 scripts/unpersist %{buildroot}%{_sbindir}
>  
> +# %{__install} -p -m0644 admin/__init__.py %{buildroot}%{python_sitelib}/nodeadmin
> +%{__install} -p -m0644 admin/configscreen.py %{buildroot}%{python_sitelib}/nodeadmin
> +%{__install} -p -m0755 admin/createdomain.py %{buildroot}%{python_sitelib}/nodeadmin
> +%{__install} -p -m0755 admin/createuser.py %{buildroot}%{python_sitelib}/nodeadmin
> +%{__install} -p -m0755 admin/definedomain.py %{buildroot}%{python_sitelib}/nodeadmin
> +%{__install} -p -m0755 admin/destroydomain.py %{buildroot}%{python_sitelib}/nodeadmin
> +%{__install} -p -m0644 admin/domainconfig.py %{buildroot}%{python_sitelib}/nodeadmin
> +%{__install} -p -m0644 admin/halworker.py %{buildroot}%{python_sitelib}/nodeadmin
> +%{__install} -p -m0644 admin/libvirtworker.py %{buildroot}%{python_sitelib}/nodeadmin
> +%{__install} -p -m0644 admin/userworker.py %{buildroot}%{python_sitelib}/nodeadmin
> +%{__install} -p -m0755 admin/listdomains.py %{buildroot}%{python_sitelib}/nodeadmin
> +%{__install} -p -m0644 admin/mainmenu.py %{buildroot}%{python_sitelib}/nodeadmin
> +%{__install} -p -m0755 admin/nodeadmin.py %{buildroot}%{python_sitelib}/nodeadmin
> +%{__install} -p -m0755 admin/undefinedomain.py %{buildroot}%{python_sitelib}/nodeadmin
> +
>  # gptsync
>  %{__install} -p -m0755 gptsync/gptsync %{buildroot}%{_sbindir}
>  %{__install} -p -m0755 gptsync/showpart %{buildroot}%{_sbindir}
> @@ -182,6 +206,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 admin/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 +353,15 @@ 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
>  %config %attr(0644,root,root) %{_sysconfdir}/ovirt-release
>  %config %attr(0644,root,root) %{_sysconfdir}/default/ovirt
>  
>   
So I've got the tool set installed, but not fully tested just yet, 
here's some things that need to be changed

python -m compileall /usr/lib/python2.6/site-packages/nodeadmin in 
common-post.ks of ovirt-node-image otherwise they get removed during 
blacklisting
 # common-blacklist.ks:find / -name '*.py' -exec rm -f {} \;

__init__.py is commented out, cant get this to copy to buildroot for 
some reason, still working on it any ideas?
 # %{__install} -p -m0644 admin/__init__.py 
%{buildroot}%{python_sitelib}/nodeadmin


missing dependencies, whether via rpm/python failures

+python-setuptools  - (needed for load_entry_point in nodeadmin)
+dbus-python           
+newt
+newt-python
+pkgconfig
+python-devel
+slang
+slang-devel   

1.36MB increase from rpms



The egg creation in ovirt-node package for nodeadmin is failing 
somewhere and the install doesnt complete during the build process, 
which requires it to be run again on boot. I don't see any obvious 
failures, what can I look for?

python admin/setup.py install --root 
/home/jboggs/rpm/BUILDROOT/ovirt-node-1.0.2-0.fc11.20090831204520git9646cd0.x86_64
running install
running build
running install_egg_info
running egg_info
creating nodeadmin.egg-info
writing nodeadmin.egg-info/PKG-INFO
writing top-level names to nodeadmin.egg-info/top_level.txt
writing dependency_links to nodeadmin.egg-info/dependency_links.txt
writing entry points to nodeadmin.egg-info/entry_points.txt
writing manifest file 'nodeadmin.egg-info/SOURCES.txt'
warning: manifest_maker: standard file 'setup.py' not found
reading manifest file 'nodeadmin.egg-info/SOURCES.txt'
writing manifest file 'nodeadmin.egg-info/SOURCES.txt'
Copying nodeadmin.egg-info to 
/home/jboggs/rpm/BUILDROOT/ovirt-node-1.0.2-0.fc11.20090831204520git9646cd0.x86_64/usr/lib/python2.6/site-packages/nodeadmin-1.0.2-py2.6.egg-info
running install_scripts
Installing definedom script to 
/home/jboggs/rpm/BUILDROOT/ovirt-node-1.0.2-0.fc11.20090831204520git9646cd0.x86_64/usr/bin
Installing undefinedom script to 
/home/jboggs/rpm/BUILDROOT/ovirt-node-1.0.2-0.fc11.20090831204520git9646cd0.x86_64/usr/bin
Installing nodeadmin script to 
/home/jboggs/rpm/BUILDROOT/ovirt-node-1.0.2-0.fc11.20090831204520git9646cd0.x86_64/usr/bin
Installing createdom script to 
/home/jboggs/rpm/BUILDROOT/ovirt-node-1.0.2-0.fc11.20090831204520git9646cd0.x86_64/usr/bin
Installing listdoms script to 
/home/jboggs/rpm/BUILDROOT/ovirt-node-1.0.2-0.fc11.20090831204520git9646cd0.x86_64/usr/bin
Installing createuser script to 
/home/jboggs/rpm/BUILDROOT/ovirt-node-1.0.2-0.fc11.20090831204520git9646cd0.x86_64/usr/bin
Installing destroydom script to 
/home/jboggs/rpm/BUILDROOT/ovirt-node-1.0.2-0.fc11.20090831204520git9646cd0.x86_64/usr/bin
+ rm -rf 
/home/jboggs/rpm/BUILDROOT/ovirt-node-1.0.2-0.fc11.20090831204520git9646cd0.x86_64/usr/lib/python2.6/site-packages/nodeadmin-1.0.2-py2.6.egg-info




More information about the ovirt-devel mailing list