On Tue, Sep 29, 2009 at 05:12:58PM -0400, Mike Burns wrote:
> On Wed, Sep 23, 2009 at 05:12:46PM -0400, Darryl L. Pierce wrote:
> > Moved the node administration elements to a separate submenu.
> > 
> > Created a new network administration menu.
> > 
> >  * users can define a network
> >  * users can create a network
> >  * users can destroy a network
> >  * users can undefine a network
> >  * users can list existing networks, including details
> > 
> > Each new command is also available as a separate command line executable
> > as well.
> > 
> > Signed-off-by: Darryl L. Pierce <[email protected]>
> > ---
> >  Makefile.am                  |    9 ++
> >  nodeadmin/configscreen.py    |   35 ++++++-
> >  nodeadmin/createdomain.py    |    3 -
> >  nodeadmin/createnetwork.py   |   52 +++++++++
> >  nodeadmin/definenet.py       |  255 
> > ++++++++++++++++++++++++++++++++++++++++++
> >  nodeadmin/destroynetwork.py  |   56 +++++++++
> >  nodeadmin/halworker.py       |    8 ++
> >  nodeadmin/libvirtworker.py   |   61 ++++++++++
> >  nodeadmin/listnetworks.py    |   55 +++++++++
> >  nodeadmin/mainmenu.py        |   71 ++++--------
> >  nodeadmin/menuscreen.py      |   57 ++++++++++
> >  nodeadmin/netmenu.py         |   58 ++++++++++
> >  nodeadmin/networkconfig.py   |   99 ++++++++++++++++
> >  nodeadmin/nodemenu.py        |   63 +++++++++++
> >  nodeadmin/setup.py.in        |    7 +-
> >  nodeadmin/undefinenetwork.py |   88 +++++++++++++++
> >  ovirt-node.spec.in           |   33 +++++-
> >  17 files changed, 948 insertions(+), 62 deletions(-)
> >  create mode 100644 nodeadmin/createnetwork.py
> >  create mode 100644 nodeadmin/definenet.py
> >  create mode 100644 nodeadmin/destroynetwork.py
> >  create mode 100644 nodeadmin/listnetworks.py
> >  create mode 100644 nodeadmin/menuscreen.py
> >  create mode 100755 nodeadmin/netmenu.py
> >  create mode 100644 nodeadmin/networkconfig.py
> >  create mode 100755 nodeadmin/nodemenu.py
> >  create mode 100644 nodeadmin/undefinenetwork.py
> > 
> > diff --git a/Makefile.am b/Makefile.am
> > index 2d195c0..abb7c33 100644
> > --- a/Makefile.am
> > +++ b/Makefile.am
> > @@ -28,17 +28,26 @@ EXTRA_DIST =                    \
> >    images/syslinux-vesa-splash.jpg  \
> >    nodeadmin/__init__.py         \
> >    nodeadmin/configscreen.py     \
> > +  nodeadmin/createnetwork.py    \
> >    nodeadmin/createuser.py       \
> >    nodeadmin/destroydomain.py    \
> > +  nodeadmin/destroynetwork.py   \
> >    nodeadmin/halworker.py        \
> >    nodeadmin/libvirtworker.py    \
> >    nodeadmin/userworker.py       \
> >    nodeadmin/mainmenu.py         \
> > +  nodeadmin/menuscreen.py       \
> > +  nodeadmin/netmenu.py          \
> > +  nodeadmin/nodemenu.py         \
> >    nodeadmin/undefinedomain.py   \
> > +  nodeadmin/undefinenetwork.py  \
> >    nodeadmin/createdomain.py     \
> >    nodeadmin/definedomain.py     \
> > +  nodeadmin/definenet.py        \
> >    nodeadmin/domainconfig.py     \
> > +  nodeadmin/networkconfig.py    \
> >    nodeadmin/listdomains.py      \
> > +  nodeadmin/listnetworks.py     \
> >    nodeadmin/nodeadmin.py        \
> >    nodeadmin/setup.py            \
> >    nodeadmin/utils.py            \
> > diff --git a/nodeadmin/configscreen.py b/nodeadmin/configscreen.py
> > index 0282eee..f214aea 100644
> > --- a/nodeadmin/configscreen.py
> > +++ b/nodeadmin/configscreen.py
> > @@ -77,8 +77,9 @@ class ConfigScreen:
> >          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)
> > +            # TODO: need to set the form height to the number of elements 
> > on the page
> > +            gridform = GridForm(screen, self.__title, 1, 10)
> >              current_element = 0
> >              for element in elements:
> >                  gridform.add(element, 0, current_element)
> > @@ -133,7 +134,7 @@ class DomainListConfigScreen(ConfigScreen):
> >          if len(domains) > 0:
> >              self.__has_domains = True
> >              self.__domain_list = Listbox(0)
> > -            for name in self.get_libvirt().list_domains(defined, created):
> > +            for name in domains:
> >                  self.__domain_list.append(name, name)
> >              result = [self.__domain_list]
> >          else:
> > @@ -148,3 +149,33 @@ class DomainListConfigScreen(ConfigScreen):
> >  
> >      def has_selectable_domains(self):
> >          return self.__has_domains
> > +
> > +class NetworkListConfigScreen(ConfigScreen):
> > +    '''Provides a base class for all config screens that require a network 
> > list.'''
> > +
> > +    def __init__(self, title):
> > +        ConfigScreen.__init__(self, title)
> > +
> > +    def get_network_list_page(self, screen, defined=True, created=True):
> > +        networks = self.get_libvirt().list_networks(defined, created)
> > +        result = None
> > +
> > +        if len(networks) > 0:
> > +            self.__has_networks = True
> > +            self.__network_list = Listbox(0)
> > +            for name in networks:
> > +                self.__network_list.append(name, name)
> > +            result = self.__network_list
> > +        else:
> > +            self.__has_networks = False
> > +            result = Label("There are no networks available.")
> > +        grid = Grid(1, 1)
> > +        grid.setField(result, 0, 0)
> > +        return [Label("Network List"),
> > +                grid]
> > +
> > +    def get_selected_network(self):
> > +        return self.__network_list.current()
> > +
> > +    def has_selectable_networks(self):
> > +        return self.__has_networks
> > diff --git a/nodeadmin/createdomain.py b/nodeadmin/createdomain.py
> > index b73a09e..6f10b44 100755
> > --- a/nodeadmin/createdomain.py
> > +++ b/nodeadmin/createdomain.py
> > @@ -55,9 +55,6 @@ class CreateDomainConfigScreen(DomainListConfigScreen):
> >              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)
> > diff --git a/nodeadmin/createnetwork.py b/nodeadmin/createnetwork.py
> > new file mode 100644
> > index 0000000..6b40bb6
> > --- /dev/null
> > +++ b/nodeadmin/createnetwork.py
> > @@ -0,0 +1,52 @@
> > +#!/usr/bin/env python
> > +#
> > +# createnetwork.py - Copyright (C) 2009 Red Hat, Inc.
> > +# Written by Darryl L. Pierce <[email protected]>
> > +#
> > +# 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 *
> > +
> > +LIST_PAGE   = 1
> > +CREATE_PAGE = 2
> > +
> > +class CreateNetworkConfigScreen(NetworkListConfigScreen):
> > +    def __init__(self):
> > +        NetworkListConfigScreen.__init__(self, "Create A Network")
> > +
> > +    def get_elements_for_page(self, screen, page):
> > +        if   page is LIST_PAGE:   return 
> > self.get_network_list_page(screen, created = False)
> > +        elif page is CREATE_PAGE: return 
> > self.get_create_network_page(screen)
> > +
> > +    def page_has_next(self, page):
> > +        if page is LIST_PAGE: return self.has_selectable_networks()
> > +
> > +    def page_has_back(self, page):
> > +        return (page is CREATE_PAGE)
> > +
> > +    def validate_input(self, page, errors):
> > +        if page is LIST_PAGE:
> > +            self.get_libvirt().create_network(self.get_selected_network())
> > +            return True
> > +
> > +    def get_create_network_page(self, screen):
> > +        return [Label("Network Started"),
> > +                Label("%s was successfully started." % 
> > self.get_selected_network())]
> > +
> > +def CreateNetwork():
> > +    screen = CreateNetworkConfigScreen()
> > +    screen.start()
> > diff --git a/nodeadmin/definenet.py b/nodeadmin/definenet.py
> > new file mode 100644
> > index 0000000..b76fe27
> > --- /dev/null
> > +++ b/nodeadmin/definenet.py
> > @@ -0,0 +1,255 @@
> > +# definenet.py - Copyright (C) 2009 Red Hat, Inc.
> > +# Written by Darryl L. Pierce <[email protected]>
> > +#
> > +# 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 IPy import IP
> > +import traceback
> > +import re
> > +import logging
> > +
> > +from configscreen  import ConfigScreen
> > +from networkconfig import NetworkConfig
> > +
> > +NETWORK_NAME_PAGE            = 1
> > +IPV4_ADDRESS_PAGE            = 2
> > +PUBLIC_NETWORK_ALERT_PAGE    = 3
> > +NETWORK_DETAILS_PAGE         = 4
> > +DHCP_RANGE_PAGE              = 5
> > +NETWORK_TYPE_PAGE            = 6
> > +SELECT_PHYSICAL_NETWORK_PAGE = 7
> > +SUMMARY_PAGE                 = 8
> > +
> > +class DefineNetworkConfigScreen(ConfigScreen):
> > +    def __init__(self):
> > +        ConfigScreen.__init__(self, "Create A Virtual Network Interface")
> > +        self.__config = NetworkConfig()
> > +
> > +    def get_elements_for_page(self, screen, page):
> > +        if   page is NETWORK_NAME_PAGE:            return 
> > self.get_network_name_page(screen)
> > +        elif page is IPV4_ADDRESS_PAGE:            return 
> > self.get_ipv4_address_page(screen)
> > +        elif page is PUBLIC_NETWORK_ALERT_PAGE:    return 
> > self.get_public_network_alert_page(screen)
> > +        elif page is NETWORK_DETAILS_PAGE:         return 
> > self.get_network_details_page(screen)
> > +        elif page is DHCP_RANGE_PAGE:              return 
> > self.get_dhcp_range_page(screen)
> > +        elif page is NETWORK_TYPE_PAGE:            return 
> > self.get_network_type_page(screen)
> > +        elif page is SELECT_PHYSICAL_NETWORK_PAGE: return 
> > self.get_select_physical_network_page(screen)
> > +        elif page is SUMMARY_PAGE:                 return 
> > self.get_summary_page(screen)
> > +
> > +    def validate_input(self, page, errors):
> > +        if page is NETWORK_NAME_PAGE:
> > +            if len(self.__name.value()) > 0:
> > +                if re.match("^[a-zA-Z0-9_]*$", self.__name.value()):
> > +                    return True
> > +                else:
> > +                    errors.append("The network name can only contain 
> > letters, numbers and the underscore, and no spaces.")
> > +            else:
> > +                errors.append("Network name must be non-blank and less 
> > than 50 characters")
> > +        elif page is IPV4_ADDRESS_PAGE:
> > +            if len(self.__ipv4_address.value()) > 0:
> > +                try:
> > +                    
> > self.__config.set_ipv4_address(self.__ipv4_address.value())
> > +                    return True
> > +                except Exception, error:
> > +                    errors.append("The network address could not be 
> > understood: %s" % str(error))
> > +            else:
> > +                errors.append("Network must be entered in the format 
> > 1.2.3.4/8")
> > +        elif page is PUBLIC_NETWORK_ALERT_PAGE: return True
> > +        elif page is NETWORK_DETAILS_PAGE: return True
> > +        elif page is DHCP_RANGE_PAGE:
> > +            try:
> > +                if len(self.__start_address.value()) > 0 and 
> > len(self.__end_address.value()) > 0:
> > +                    start = IP(self.__start_address.value(), )
> > +                    end   = IP(self.__end_address.value())
> > +                    if not self.__config.is_bad_address(start) and not 
> > self.__config.is_bad_address(end):
> > +                        return True
> > +                    else:
> > +                        errors.append("Start and/or end address are 
> > outside of the choosen network.")
> > +                else:
> > +                    errors.append("Start and end address must be 
> > non-blank.")
> > +            except Exception, error:
> > +                logging.error(str(error))
> > +                errors.append("The start and/or end addresses could not be 
> > understood.")
> > +        elif page is NETWORK_TYPE_PAGE: return True
> > +        elif page is SELECT_PHYSICAL_NETWORK_PAGE: return True
> > +        elif page is SUMMARY_PAGE: return True
> > +        return False
> > +
> > +    def process_input(self, page):
> > +        if page is NETWORK_NAME_PAGE:
> > +            self.__config.set_name(self.__name.value())
> > +        elif page is DHCP_RANGE_PAGE:
> > +            
> > self.__config.set_ipv4_start_address(self.__start_address.value())
> > +            self.__config.set_ipv4_end_address(self.__end_address.value())
> > +        elif page is NETWORK_TYPE_PAGE:
> > +            
> > self.__config.set_isolated_network(self.__isolated_network.value())
> > +        elif page is SELECT_PHYSICAL_NETWORK_PAGE:
> > +            
> > self.__config.set_physical_device(self.__physical_devices.getSelection())
> > +        elif page is SUMMARY_PAGE:
> > +            self.get_libvirt().define_network(self.__config)
> > +            self.set_finished()
> > +
> > +    def get_next_page(self, page):
> > +        if page is IPV4_ADDRESS_PAGE:
> > +            if self.__config.is_public_ipv4_network():
> > +                return PUBLIC_NETWORK_ALERT_PAGE
> > +            else:
> > +                return NETWORK_DETAILS_PAGE
> > +        if page is NETWORK_TYPE_PAGE:
> > +            if self.__config.is_isolated_network():
> > +                return SUMMARY_PAGE
> > +            else:
> > +                return SELECT_PHYSICAL_NETWORK_PAGE
> > +        return ConfigScreen.get_next_page(self, page)
> > +
> > +    def get_back_page(self, page):
> > +        if page is NETWORK_DETAILS_PAGE:
> > +            return IPV4_ADDRESS_PAGE
> > +        if page is SUMMARY_PAGE:
> > +            if self.__config.is_isolated_network():
> > +                return NETWORK_TYPE_PAGE
> > +            else:
> > +                return SELECT_PHYSICAL_NETWORK_PAGE
> > +        return ConfigScreen.get_back_page(self, page)
> > +
> > +    def page_has_finish(self, page):
> > +        if page is SUMMARY_PAGE: return True
> > +        return False
> > +
> > +    def page_has_next(self, page):
> > +        if page < SUMMARY_PAGE: return True
> > +
> > +    def page_has_back(self, page):
> > +        if page > NETWORK_NAME_PAGE: return True
> > +        return False
> > +
> > +    def get_network_name_page(self, screen):
> > +        self.__name = Entry(50, self.__config.get_name())
> > +        grid = Grid(2, 1)
> > +        grid.setField(Label("Network Name:"), 0, 0)
> > +        grid.setField(self.__name, 1, 0)
> > +        return [Label("Please choose a name for your virtual network"),
> > +                grid]
> > +
> > +    def get_ipv4_address_page(self, screen):
> > +        self.__ipv4_address = Entry(18, self.__config.get_ipv4_address())
> > +        grid = Grid(2, 1)
> > +        grid.setField(Label("Network:"), 0, 0, anchorRight = 1)
> > +        grid.setField(self.__ipv4_address, 1, 0, anchorLeft = 1)
> > +        return [Label("You will need to choose an IPv4 address space for 
> > the virtual network:"),
> > +                grid,
> > +                Label("HINT: The network should be chosen from"),
> > +                Label("one of the IPv4 private address ranges;"),
> > +                Label("e.g., 10.0.0.0/8, 172.168.0.0/12, 192.168.0.0/16")]
> > +
> > +    def get_network_details_page(self, screen):
> > +        grid = Grid(2, 6)
> > +        grid.setField(Label("Network:"), 0, 0, anchorRight = 1)
> > +        grid.setField(Label(self.__config.get_ipv4_address()), 1, 0, 
> > anchorLeft = 1)
> > +        grid.setField(Label("Netmask:"), 0, 1, anchorRight = 1)
> > +        grid.setField(Label(self.__config.get_ipv4_netmask()), 1, 1, 
> > anchorLeft = 1)
> > +        grid.setField(Label("Broadcast:"), 0, 2, anchorRight = 1)
> > +        grid.setField(Label(self.__config.get_ipv4_broadcast()), 1, 2, 
> > anchorLeft = 1)
> > +        grid.setField(Label("Gateway:"), 0, 3, anchorRight = 1)
> > +        grid.setField(Label(self.__config.get_ipv4_gateway()), 1, 3, 
> > anchorLeft = 1)
> > +        grid.setField(Label("Size:"), 0, 4, anchorRight = 1)
> > +        grid.setField(Label("%d addresses" % 
> > self.__config.get_ipv4_max_addresses()), 1, 4, anchorLeft = 1)
> > +        grid.setField(Label("Type:"), 0, 5, anchorRight = 1)
> > +        grid.setField(Label(self.__config.get_ipv4_network_type()), 1, 5, 
> > anchorLeft = 1)
> > +        return [Label("Network Details"),
> > +                grid]
> > +
> > +    def get_public_network_alert_page(self, screen):
> > +        grid = Grid(1, 2)
> > +        grid.setField(Label("The network should normally use a private 
> > IPv4 address."), 0, 0, anchorLeft = 1)
> > +        grid.setField(Label("Use this non-private address anyway?"), 0, 1, 
> > anchorLeft = 1)
> > +        return [Label("Check Network Address"),
> > +                grid]
> > +
> > +    def get_dhcp_range_page(self, screen):
> > +        self.__start_address = Entry(15, 
> > self.__config.get_ipv4_start_address())
> > +        self.__end_address   = Entry(15, 
> > self.__config.get_ipv4_end_address())
> > +        grid = Grid(2,2)
> > +        grid.setField(Label("Start:"), 0, 0, anchorRight = 1)
> > +        grid.setField(self.__start_address, 1, 0, anchorLeft = 1)
> > +        grid.setField(Label("End:"), 0, 1, anchorRight = 1)
> > +        grid.setField(self.__end_address, 1, 1, anchorLeft = 1)
> 
> If possible we might want to expand the size of these fields 1 char.  If you 
> have a max width IP address, the first char gets cut off from the display.  
> Functionally, this is no impact, but it was confusing at first glance.
> 
> 
> > +        return [Label("Selecting The DHCP Range"),
> > +                grid,
> > +                Label("TIP: Unless you wish to reserve some addresses to 
> > allow static network"),
> > +                Label("configuration in virtual machines, these paraemters 
> > can be left with"),
> > +                Label("their default values.")]
> > +
> > +    def get_network_type_page(self, screen):
> > +        self.__isolated_network = Checkbox("Isolated virtual network",
> > +                                           
> > self.__config.is_isolated_network())
> > +        grid = Grid(1, 2)
> > +        grid.setField(Label("Please indicate whether this virtual network 
> > should be connected to the physical network."), 0, 0)
> 
> On my vm, this label was too wide for the screen.  I'm not sure if this is 
> only my system, but we might want to split into multiple lines.
> 
> > +        grid.setField(self.__isolated_network, 0, 1)
> > +        return [Label("Connecting To Physical Network"),
> > +                grid]
> > +
> > +    def get_select_physical_network_page(self, screen):
> > +        devices = []
> > +        devices.append(["NAT to any physical device", "", 
> > self.__config.get_physical_device() == ""])
> > +        for device in self.get_hal().list_network_devices():
> > +            devices.append(["NAT to physical device %s" % device, device, 
> > self.__config.get_physical_device() == device])
> > +        self.__physical_devices = RadioBar(screen, (devices))
> > +        grid = Grid(1, 2)
> > +        grid.setField(Label("Forward to physical network:"), 0, 0)
> > +        grid.setField(self.__physical_devices, 0, 1)
> > +        return [Label("Connecting To Physical Network"),
> > +                grid]
> > +
> > +    def get_summary_page(self, screen):
> > +        grid1 = Grid(2, 1)
> > +        grid1.setField(Label("Network name:"), 0, 0, anchorRight = 1)
> > +        grid1.setField(Label(self.__config.get_name()), 1, 0, anchorLeft = 
> > 1)
> > +
> > +        grid2 = Grid(2, 3)
> > +        grid2.setField(Label("Network:"), 0, 0, anchorRight = 1)
> > +        grid2.setField(Label(self.__config.get_ipv4_address()), 1, 0, 
> > anchorLeft = 1)
> > +        grid2.setField(Label("Gateway:"), 0, 1, anchorRight = 1)
> > +        grid2.setField(Label(self.__config.get_ipv4_gateway()), 1, 1, 
> > anchorLeft = 1)
> > +        grid2.setField(Label("Netmask:"), 0, 2, anchorRight = 1)
> > +        grid2.setField(Label(self.__config.get_ipv4_netmask()), 1, 2, 
> > anchorLeft = 1)
> > +
> > +        grid3 = Grid(2, 2)
> > +        grid3.setField(Label("Start address:"), 0, 0, anchorRight = 1)
> > +        grid3.setField(Label(self.__config.get_ipv4_start_address()), 1, 
> > 0, anchorLeft = 1)
> > +        grid3.setField(Label("End address:"), 0, 1, anchorRight = 1)
> > +        grid3.setField(Label(self.__config.get_ipv4_end_address()), 1, 1, 
> > anchorLeft = 1)
> > +
> > +        grid4 = Grid(2, 1)
> > +        grid4.setField(Label("Connectivity:"), 0, 0, anchorRight = 1)
> > +        if self.__config.is_isolated_network():
> > +            grid4.setField(Label("Isolated virtual network"), 1, 0, 
> > anchorLeft = 1)
> > +        else:
> > +            grid4.setField(Label("NAT to %s" % 
> > self.__config.get_physical_device_text()), 1, 0, anchorLeft = 1)
> > +
> > +        return [Label("Ready To Create Network"),
> > +                Label("Summary"),
> > +                grid1,
> > +                Label("IPv4 Network"),
> > +                grid2,
> > +                Label("DHCP"),
> > +                grid3,
> > +                Label("Forwarding"),
> > +                grid4]
> > +
> > +def DefineNetwork():
> > +    screen = DefineNetworkConfigScreen()
> > +    screen.start()
> > diff --git a/nodeadmin/destroynetwork.py b/nodeadmin/destroynetwork.py
> > new file mode 100644
> > index 0000000..5fb3e82
> > --- /dev/null
> > +++ b/nodeadmin/destroynetwork.py
> > @@ -0,0 +1,56 @@
> > +#!/usr/bin/env python
> > +#
> > +# destroynetwork.py - Copyright (C) 2009 Red Hat, Inc.
> > +# Written by Darryl L. Pierce <[email protected]>
> > +#
> > +# 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 *
> > +
> > +LIST_PAGE    = 1
> > +DESTROY_PAGE = 2
> > +
> > +class DestroyNetworkConfigScreen(NetworkListConfigScreen):
> > +    def __init__(self):
> > +        NetworkListConfigScreen.__init__(self, "Destroy A Network")
> > +
> > +    def get_elements_for_page(self, screen, page):
> > +        if   page is LIST_PAGE:    return 
> > self.get_network_list_page(screen, defined = False)
> > +        elif page is DESTROY_PAGE: return 
> > self.get_destroy_network_page(screen)
> > +
> > +    def page_has_next(self, page):
> > +        if page is LIST_PAGE: return self.has_selectable_networks()
> > +        return False
> > +
> > +    def page_has_back(self, page):
> > +        if page is DESTROY_PAGE: return True
> > +        return False
> > +
> > +    def validate_input(self, page, errors):
> > +        if page is LIST_PAGE:
> > +            network = self.get_selected_network()
> > +            self.get_libvirt().destroy_network(network)
> > +            return True
> > +        return False
> > +
> > +    def get_destroy_network_page(self, screen):
> > +        return [Label("Network Destroyed"),
> > +                Label("%s has been destroyed." % 
> > self.get_selected_network())]
> > +
> > +def DestroyNetwork():
> > +    screen = DestroyNetworkConfigScreen()
> > +    screen.start()
> > diff --git a/nodeadmin/halworker.py b/nodeadmin/halworker.py
> > index 448c22d..7aa9a04 100644
> > --- a/nodeadmin/halworker.py
> > +++ b/nodeadmin/halworker.py
> > @@ -35,3 +35,11 @@ class HALWorker:
> >                  if info.GetProperty("volume.disc.has_data"):
> >                      result[str(info.GetProperty("block.device"))] = 
> > info.GetProperty("volume.label")
> >          return result
> > +
> > +    def list_network_devices(self):
> > +        result = []
> > +        for udi in self.__conn.FindDeviceByCapability("net"):
> > +            device = self.__bus.get_object("org.freedesktop.Hal", udi)
> > +            info = dbus.Interface(device, "org.freedesktop.Hal.Device")
> > +            result.append(info.GetProperty("net.interface"))
> > +        return result
> > diff --git a/nodeadmin/libvirtworker.py b/nodeadmin/libvirtworker.py
> > index adaea16..ba07605 100644
> > --- a/nodeadmin/libvirtworker.py
> > +++ b/nodeadmin/libvirtworker.py
> > @@ -73,6 +73,67 @@ class LibvirtWorker:
> >          domain = self.get_domain(name)
> >          domain.undefine()
> >  
> > +    def list_networks(self, defined = True, started = True):
> > +        '''Lists all networks.'''
> > +        result = []
> > +        if defined: result.extend(self.__conn.listDefinedNetworks())
> > +        if started: result.extend(self.__conn.listNetworks())
> > +        return result
> > +
> > +    def get_network(self, name):
> > +        '''Returns the specified network.'''
> > +        result = self.__conn.networkLookupByName(name)
> > +        if result is None: raise Exception("No such network exists: %s" % 
> > name)
> > +
> > +        return result
> > +
> > +    def network_exists(self, name):
> > +        '''Returns if a network with the given name already exists.'''
> > +        networks = self.list_networks()
> > +        if name in networks: return True
> > +        return False
> > +
> > +    def define_network(self, config):
> > +        '''Defines a new network.'''
> > +        # since there's no other way currently, we'll have to use XML
> > +        name = config.get_name()
> > +        ip = config.get_ipv4_address_raw()
> > +        start = config.get_ipv4_start_address()
> > +        end = config.get_ipv4_end_address()
> > +        fw = config.get_physical_device()
> > +
> > +        xml = "<network>" + \
> > +              "  <name>%s</name>\n" % name
> > +        if not config.is_public_ipv4_network():
> > +            if fw is not "":
> > +                xml += "  <forward dev='%s'/>\n" % fw[1]
> > +            else:
> > +                xml += "  <forward/>\n"
> > +
> > +        xml += "  <ip address='%s' netmask='%s'>\n" % (str(ip[1]), 
> > str(ip.netmask()))
> > +        xml += "    <dhcp>\n"
> > +        xml += "      <range start='%s' end='%s'/>\n" % (str(start), 
> > str(end))
> > +        xml += "    </dhcp>\n"
> > +        xml += "  </ip>\n"
> > +        xml += "</network>\n"
> > +
> > +        self.__conn.networkDefineXML(xml)
> > +
> > +    def create_network(self, name):
> > +        '''Creates a defined network.'''
> > +        network = self.get_network(name)
> > +        network.create()
> > +
> > +    def destroy_network(self, name):
> > +        '''Destroys the specified network.'''
> > +        network = self.get_network(name)
> > +        network.destroy()
> > +
> > +    def undefine_network(self, name):
> > +        '''Undefines the specified network.'''
> > +        network = self.get_network(name)
> > +        network.undefine()
> > +
> >      def list_storage_pools(self):
> >          '''Returns the list of all defined storage pools.'''
> >          return self.__conn.listStoragePools()
> > diff --git a/nodeadmin/listnetworks.py b/nodeadmin/listnetworks.py
> > new file mode 100644
> > index 0000000..a53f898
> > --- /dev/null
> > +++ b/nodeadmin/listnetworks.py
> > @@ -0,0 +1,55 @@
> > +# listnetworks.py - Copyright (C) 2009 Red Hat, Inc.
> > +# Written by Darryl L. Pierce <[email protected]>
> > +#
> > +# 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 configscreen import *
> > +
> > +LIST_PAGE    = 1
> > +DETAILS_PAGE = 2
> > +
> > +class ListNetworksConfigScreen(NetworkListConfigScreen):
> > +    def __init__(self):
> > +        NetworkListConfigScreen.__init__(self, "List Networks")
> > +
> > +    def page_has_next(self, page):
> > +        return (page is LIST_PAGE) and self.has_selectable_networks()
> > +
> > +    def page_has_back(self, page):
> > +        return (page is DETAILS_PAGE)
> > +
> > +    def get_elements_for_page(self, screen, page):
> > +        if   page is LIST_PAGE:    return 
> > self.get_network_list_page(screen)
> > +        elif page is DETAILS_PAGE: return 
> > self.get_network_details_page(screen)
> > +
> > +    def get_network_details_page(self, screen):
> > +        network = 
> > self.get_libvirt().get_network(self.get_selected_network())
> > +        grid = Grid(2, 3)
> > +        grid.setField(Label("Name:"), 0, 0, anchorRight = 1)
> > +        grid.setField(Label(network.name()), 1, 0, anchorLeft = 1)
> > +        grid.setField(Label("Autostart:"), 0, 1, anchorRight = 1)
> > +        label = "No"
> > +        if network.autostart(): label = "Yes"
> > +        grid.setField(Label(label), 1, 1, anchorLeft = 1)
> > +        if network.bridgeName() is not "":
> > +            grid.setField(Label("Bridge:"), 0, 2, anchorRight = 1)
> > +            grid.setField(Label(network.bridgeName()), 1, 2, anchorLeft = 
> > 1)
> > +        return [Label("Network Interface Details"),
> > +                grid]
> > +
> > +def ListNetworks():
> > +    screen = ListNetworksConfigScreen()
> > +    screen.start()
> > diff --git a/nodeadmin/mainmenu.py b/nodeadmin/mainmenu.py
> > index 497ad57..73501fa 100755
> > --- a/nodeadmin/mainmenu.py
> > +++ b/nodeadmin/mainmenu.py
> > @@ -18,57 +18,30 @@
> >  
> >  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
> > +
> > +from menuscreen     import MenuScreen
> > +from nodemenu       import NodeMenu
> > +from netmenu        import NetworkMenu
> > +
> >  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
> > +NODE_MENU    = 1
> > +NETWORK_MENU = 2
> > +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()
> > +class MainMenuScreen(MenuScreen):
> > +    def __init__(self):
> > +        MenuScreen.__init__(self, "Main Menu")
> >  
> > -        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
> > +    def get_menu_items(self):
> > +        return (("Node Administration", NODE_MENU),
> > +                ("Network Administration", NETWORK_MENU))
> > +
> > +    def handle_selection(self, page):
> > +        if   page is NODE_MENU:    NodeMenu()
> > +        elif page is NETWORK_MENU: NetworkMenu()
> > +
> > +def MainMenu():
> > +    screen = MainMenuScreen()
> > +    screen.start()
> > diff --git a/nodeadmin/menuscreen.py b/nodeadmin/menuscreen.py
> > new file mode 100644
> > index 0000000..1700e8c
> > --- /dev/null
> > +++ b/nodeadmin/menuscreen.py
> > @@ -0,0 +1,57 @@
> > +# mainmenu.py - Copyright (C) 2009 Red Hat, Inc.
> > +# Written by Darryl L. Pierce <[email protected]>
> > +#
> > +# 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
> > +
> > +import utils
> > +import logging
> > +
> > +EXIT_MENU = 99
> > +
> > +class MenuScreen:
> > +    def __init__(self, title):
> > +        self.__title = title
> > +
> > +    def start(self):
> > +        finished = False
> > +        while finished == False:
> > +            screen = SnackScreen()
> > +            menu = Listbox(height = 0, width = 0, returnExit = 1)
> > +            for menu_item in self.get_menu_items():
> > +                menu.append(menu_item[0], menu_item[1])
> > +            menu.append("Exit Menu", EXIT_MENU)
> > +            gridform = GridForm(screen, self.__title, 1, 4)
> > +            gridform.add(menu, 0, 0)
> > +            result = gridform.run();
> > +            screen.popWindow()
> > +            screen.finish()
> > +
> > +            try:
> > +                if result.current() == EXIT_MENU: finished = True
> > +                else: self.handle_selection(result.current())
> > +            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/netmenu.py b/nodeadmin/netmenu.py
> > new file mode 100755
> > index 0000000..88e159f
> > --- /dev/null
> > +++ b/nodeadmin/netmenu.py
> > @@ -0,0 +1,58 @@
> > +# mainmenu.py - Copyright (C) 2009 Red Hat, Inc.
> > +# Written by Darryl L. Pierce <[email protected]>
> > +#
> > +# 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 menuscreen      import MenuScreen
> > +from definenet       import DefineNetwork
> > +from createnetwork   import CreateNetwork
> > +from destroynetwork  import DestroyNetwork
> > +from undefinenetwork import UndefineNetwork
> > +from listnetworks    import ListNetworks
> > +
> > +import utils
> > +import logging
> > +
> > +DEFINE_NETWORK   = 1
> > +CREATE_NETWORK   = 2
> > +DESTROY_NETWORK  = 3
> > +UNDEFINE_NETWORK = 4
> > +LIST_NETWORKS    = 5
> > +
> > +class NetworkMenuScreen(MenuScreen):
> > +    def __init__(self):
> > +        MenuScreen.__init__(self, "Network Administration")
> > +
> > +    def get_menu_items(self):
> > +        return (("Define A Network",   DEFINE_NETWORK),
> > +                ("Create A Network",   CREATE_NETWORK),
> > +                ("Destroy A Network",  DESTROY_NETWORK),
> > +                ("Undefine A Network", UNDEFINE_NETWORK),
> > +                ("List Networks",      LIST_NETWORKS))
> > +
> > +    def handle_selection(self, item):
> > +        if   item is DEFINE_NETWORK:   DefineNetwork()
> > +        elif item is CREATE_NETWORK:   CreateNetwork()
> > +        elif item is DESTROY_NETWORK:  DestroyNetwork()
> > +        elif item is UNDEFINE_NETWORK: UndefineNetwork()
> > +        elif item is LIST_NETWORKS:    ListNetworks()
> > +
> > +def NetworkMenu():
> > +    screen = NetworkMenuScreen()
> > +    screen.start()
> > diff --git a/nodeadmin/networkconfig.py b/nodeadmin/networkconfig.py
> > new file mode 100644
> > index 0000000..57d184d
> > --- /dev/null
> > +++ b/nodeadmin/networkconfig.py
> > @@ -0,0 +1,99 @@
> > +# networkconfig.py - Copyright (C) 2009 Red Hat, Inc.
> > +# Written by Darryl L. Pierce <[email protected]>
> > +#
> > +# 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 IPy import IP
> > +import logging
> > +
> > +class NetworkConfig:
> > +    def __init__(self):
> > +        self.__name = ""
> > +        self.set_ipv4_address("192.168.100.0/24")
> > +        self.__isolated_network = True
> > +        self.__physical_device = ""
> > +
> > +    def set_name(self, name):
> > +        self.__name = name
> > +
> > +    def get_name(self):
> > +        return self.__name
> > +
> > +    def set_ipv4_address(self, address):
> > +        self.__ipv4_address = IP(address)
> > +        start = int(self.__ipv4_address.len() / 2)
> > +        end   = self.__ipv4_address.len() - 2
> > +        self.__ipv4_start = str(self.__ipv4_address[start])
> > +        self.__ipv4_end   = str(self.__ipv4_address[end])
> > +
> > +    def get_ipv4_address(self):
> > +        return self.__ipv4_address.strNormal()
> > +
> > +    def get_ipv4_address_raw(self):
> > +        return self.__ipv4_address
> > +
> > +    def get_ipv4_netmask(self):
> > +        return self.__ipv4_address.netmask().strNormal()
> > +
> > +    def get_ipv4_broadcast(self):
> > +        return self.__ipv4_address.broadcast().strNormal()
> > +
> > +    def get_ipv4_gateway(self):
> > +        return str(self.__ipv4_address[1])
> > +
> > +    def get_ipv4_max_addresses(self):
> > +        return self.__ipv4_address.len()
> > +
> > +    def get_ipv4_network_type(self):
> > +        return self.__ipv4_address.iptype()
> > +
> > +    def is_public_ipv4_network(self):
> > +        if self.__ipv4_address.iptype() is "PUBLIC":
> > +            return True
> > +        return False
> > +
> > +    def set_ipv4_start_address(self, address):
> > +        self.__ipv4_start = address
> > +
> > +    def get_ipv4_start_address(self):
> > +        return self.__ipv4_start
> > +
> > +    def set_ipv4_end_address(self, address):
> > +        self.__ipv4_end = address
> > +
> > +    def get_ipv4_end_address(self):
> > +        return self.__ipv4_end
> > +
> > +    def is_bad_address(self, address):
> > +        return not self.__ipv4_address.overlaps(address)
> > +
> > +    def set_isolated_network(self, isolated):
> > +        self.__isolated_network = isolated
> > +
> > +    def is_isolated_network(self):
> > +        return self.__isolated_network
> > +
> > +    def set_physical_device(self, device):
> > +        self.__physical_device = device
> > +
> > +    def get_physical_device(self):
> > +        return self.__physical_device
> > +
> > +    def get_physical_device_text(self):
> > +        if self.__physical_device == "":
> > +            return "any physical device"
> > +        else:
> > +            return "physical device %s" % self.__physical_device
> > diff --git a/nodeadmin/nodemenu.py b/nodeadmin/nodemenu.py
> > new file mode 100755
> > index 0000000..9e339ff
> > --- /dev/null
> > +++ b/nodeadmin/nodemenu.py
> > @@ -0,0 +1,63 @@
> > +# mainmenu.py - Copyright (C) 2009 Red Hat, Inc.
> > +# Written by Darryl L. Pierce <[email protected]>
> > +#
> > +# 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 menuscreen     import MenuScreen
> > +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
> > +
> > +class NodeMenuScreen(MenuScreen):
> > +    def __init__(self):
> > +        MenuScreen.__init__(self, "Node Administration")
> > +
> > +    def get_menu_items(self):
> > +        return (("Define A Domain",        DEFINE_DOMAIN),
> > +                ("Create A Domain",        CREATE_DOMAIN),
> > +                ("Destroy A Domain",       DESTROY_DOMAIN),
> > +                ("Undefine A Domain",      UNDEFINE_DOMAIN),
> > +                ("List All Domains",       LIST_DOMAINS),
> > +                ("Create A User",          CREATE_USER))
> > +
> > +    def handle_selection(self, item):
> > +            if   item is DEFINE_DOMAIN:   DefineDomain()
> > +            elif item is CREATE_DOMAIN:   CreateDomain()
> > +            elif item is DESTROY_DOMAIN:  DestroyDomain()
> > +            elif item is UNDEFINE_DOMAIN: UndefineDomain()
> > +            elif item is LIST_DOMAINS:    ListDomains()
> > +            elif item is CREATE_USER:     CreateUser()
> > +
> > +def NodeMenu():
> > +    screen = NodeMenuScreen()
> > +    screen.start()
> > diff --git a/nodeadmin/setup.py.in b/nodeadmin/setup.py.in
> > index f51a34c..3635810 100644
> > --- a/nodeadmin/setup.py.in
> > +++ b/nodeadmin/setup.py.in
> > @@ -30,5 +30,10 @@ setup(name = "nodeadmin",
> >              'destroydom  = nodeadmin.destroydomain:DestroyDomain',
> >              'undefinedom = nodeadmin.undefinedomain:UndefineDomain',
> >              'createuser  = nodeadmin.createuser:CreateUser',
> > -            'listdoms    = nodeadmin.listdomains:ListDomains']
> > +            'listdoms    = nodeadmin.listdomains:ListDomains',
> > +            'definenet   = nodeadmin.definenet:DefineNetwork',
> > +            'createnet   = nodeadmin.createnetwork:CreateNetwork',
> > +            'destroynet  = nodeadmin.destroynetwork:DestroyNetwork',
> > +            'undefinenet = nodeadmin.undefinenetwork:UndefineNetwork',
> > +            'listnets    = nodeadmin.listnetworks:ListNetworks']
> >          })
> > diff --git a/nodeadmin/undefinenetwork.py b/nodeadmin/undefinenetwork.py
> > new file mode 100644
> > index 0000000..f71bd20
> > --- /dev/null
> > +++ b/nodeadmin/undefinenetwork.py
> > @@ -0,0 +1,88 @@
> > +#!/usr/bin/env python
> > +#
> > +# undefinenetwork.py - Copyright (C) 2009 Red Hat, Inc.
> > +# Written by Darryl L. Pierce <[email protected]>
> > +#
> > +# 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 *
> > +
> > +LIST_PAGE     = 1
> > +CONFIRM_PAGE  = 2
> > +UNDEFINE_PAGE = 3
> > +
> > +class UndefineNetworkConfigScreen(NetworkListConfigScreen):
> > +    def __init__(self):
> > +        NetworkListConfigScreen.__init__(self, "Undefine A Network")
> > +
> > +    def get_elements_for_page(self, screen, page):
> > +        if   page is LIST_PAGE:     return 
> > self.get_network_list_page(screen, created = False)
> > +        elif page is CONFIRM_PAGE:  return self.get_confirm_page(screen)
> > +        elif page is UNDEFINE_PAGE: return 
> > self.get_undefine_network_page(screen)
> > +
> > +    def process_input(self, page, errors):
> > +        if page is LIST_PAGE:
> > +            network = self.get_selected_network()
> > +            self.get_libvirt().undefine_network(network)
> > +            return True
> > +
> > +    def page_has_next(self, page):
> > +        if page is LIST_PAGE:    return self.has_selectable_networks()
> > +        if page is CONFIRM_PAGE: return True
> > +        return False
> > +
> > +    def page_has_back(self, page):
> > +        if page is CONFIRM_PAGE: return True
> > +        if page is UNDEFINE_PAGE: return True
> > +        return False
> > +
> > +    def get_back_page(self, page):
> > +        if   page is CONFIRM_PAGE: return LIST_PAGE
> > +        elif page is UNDEFINE_PAGE: return LIST_PAGE
> > +
> > +    def validate_input(self, page, errors):
> > +        if   page is LIST_PAGE: return True
> > +        elif page is CONFIRM_PAGE:
> > +            if self.__confirm_undefine.value():
> > +                return True
> > +            else:
> > +                errors.append("You must confirm undefining %s." % 
> > self.get_selected_network())
> > +        elif page is UNDEFINE_PAGE: return True
> > +        return False
> > +
> > +    def process_input(self, page):
> > +        if   page is LIST_PAGE:     return True
> > +        elif page is CONFIRM_PAGE:
> > +            network = self.get_selected_network()
> > +            self.get_libvirt().undefine_network(network)
> > +            return True
> > +        elif page is UNDEFINE_PAGE: return True
> > +        return False
> > +
> > +    def get_confirm_page(self, screen):
> > +        self.__confirm_undefine = Checkbox("Check here to confirm 
> > undefining %s." % self.get_selected_network(), 0)
> > +        grid = Grid(1, 1)
> > +        grid.setField(self.__confirm_undefine, 0, 0)
> > +        return [grid]
> > +
> > +    def get_undefine_network_page(self, screen):
> > +        return [Label("Network Is Undefined"),
> > +                Label("%s has been undefined." % 
> > self.get_selected_network())]
> > +
> 
> This label is awkward.  Something like "Network %s has been undefined." seems 
> better to me.
> 
> > +def UndefineNetwork():
> > +    screen = UndefineNetworkConfigScreen()
> > +    screen.start()
> > diff --git a/ovirt-node.spec.in b/ovirt-node.spec.in
> > index ee1942b..2a6b7b6 100644
> > --- a/ovirt-node.spec.in
> > +++ b/ovirt-node.spec.in
> > @@ -51,6 +51,7 @@ Requires:       anyterm
> >  Requires:       newt-python
> >  Requires:       libuser-python
> >  Requires:       dbus-python
> > +Requires:       python-IPy
> >  
> >  ExclusiveArch:  %{ix86} x86_64
> >  
> > @@ -175,19 +176,32 @@ cd -
> >  
> >  %{__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 -m0644 nodeadmin/menuscreen.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> > +%{__install} -p -m0755 nodeadmin/utils.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> > +
> > +%{__install} -p -m0755 nodeadmin/nodeadmin.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> > +%{__install} -p -m0644 nodeadmin/mainmenu.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> > +
> > +%{__install} -p -m0644 nodeadmin/nodemenu.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> >  %{__install} -p -m0755 nodeadmin/definedomain.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> > +%{__install} -p -m0755 nodeadmin/createdomain.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> >  %{__install} -p -m0755 nodeadmin/destroydomain.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> > +%{__install} -p -m0755 nodeadmin/undefinedomain.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> > +%{__install} -p -m0755 nodeadmin/listdomains.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> >  %{__install} -p -m0644 nodeadmin/domainconfig.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> > +
> > +%{__install} -p -m0644 nodeadmin/netmenu.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> > +%{__install} -p -m0644 nodeadmin/networkconfig.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> > +%{__install} -p -m0755 nodeadmin/definenet.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> > +%{__install} -p -m0755 nodeadmin/createnetwork.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> > +%{__install} -p -m0755 nodeadmin/destroynetwork.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> > +%{__install} -p -m0755 nodeadmin/undefinenetwork.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> > +
> > +%{__install} -p -m0755 nodeadmin/createuser.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> > +
> >  %{__install} -p -m0644 nodeadmin/halworker.py 
> > %{buildroot}%{python_sitelib}/nodeadmin
> >  %{__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}
> > @@ -360,6 +374,11 @@ fi
> >  %{_bindir}/destroydom
> >  %{_bindir}/undefinedom
> >  %{_bindir}/listdoms
> > +%{_bindir}/definenet
> > +%{_bindir}/createnet
> > +%{_bindir}/destroynet
> > +%{_bindir}/undefinenet
> > +%{_bindir}/listnets
> >  %{_bindir}/createuser
> >  %{_sysconfdir}/collectd.conf.in
> >  %{python_sitelib}/nodeadmin
> > -- 
> > 1.6.2.5
> > 
> > _______________________________________________
> > Ovirt-devel mailing list
> > [email protected]
> > https://www.redhat.com/mailman/listinfo/ovirt-devel
> > 
> 
> 
> This seems pretty good.  There are a couple of nitpicks inline.  The only 
> major thing I found is that we need to add /var/lib/dnsmasq to the r/o 
> filesystem overrides.  If we don't we can't create or destroy networks.  
> 
> I tested defining and undefining networks, but without above entry to rwtab, 
> i can't create/destroy networks.  I'll try rebuilding it with entry in rwtab, 
> but wanted to get comments out first.
> 
> Oh, side note, I'm not an expert at python by any stretch, so most of the 
> testing was done using black box techniques.
> 
> Mike

Ok, this works when you apply the patch that I will post to the list shortly. 

ACK, with the above comments.

_______________________________________________
Ovirt-devel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/ovirt-devel

Reply via email to