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) + 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) + 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())] + +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
