From f709cbcac9d55016647b7ff9b7ba29f9cd03c320 Mon Sep 17 00:00:00 2001 From: Apollon Oikonomopoulos <apollon@noc.grnet.gr> Date: Mon, 18 Apr 2011 18:03:35 +0300 Subject: [PATCH] Add gnt-network client script gnt-network manipulates network objects. Required parameters have been added to ganeti.cli. Signed-off-by: Apollon Oikonomopoulos <apollon@noc.grnet.gr> --- Makefile.am | 4 + lib/cli.py | 30 ++++- lib/client/gnt_network.py | 274 ++++++++++++++++++++++++++++++++++++++ man/gnt-network.rst | 154 +++++++++++++++++++++ 4 files changed, 461 insertions(+), 1 deletion(-) create mode 100644 lib/client/gnt_network.py create mode 100644 man/gnt-network.rst diff --git a/Makefile.am b/Makefile.am index 865df3ebe..6f621a999 100644 --- a/Makefile.am +++ b/Makefile.am @@ -172,6 +172,7 @@ client_PYTHON = \ lib/client/gnt_instance.py \ lib/client/gnt_job.py \ lib/client/gnt_node.py \ + lib/client/gnt_network.py \ lib/client/gnt_os.py hypervisor_PYTHON = \ @@ -318,6 +319,7 @@ gnt_scripts = \ scripts/gnt-group \ scripts/gnt-instance \ scripts/gnt-job \ + scripts/gnt-network \ scripts/gnt-node \ scripts/gnt-os @@ -333,6 +335,7 @@ PYTHON_BOOTSTRAP = \ scripts/gnt-group \ scripts/gnt-instance \ scripts/gnt-job \ + scripts/gnt-network \ scripts/gnt-node \ scripts/gnt-os @@ -441,6 +444,7 @@ man_MANS = \ man/gnt-cluster.8 \ man/gnt-debug.8 \ man/gnt-group.8 \ + man/gnt-network.8 \ man/gnt-instance.8 \ man/gnt-job.8 \ man/gnt-node.8 \ diff --git a/lib/cli.py b/lib/cli.py index 3c889c968..9d304055b 100644 --- a/lib/cli.py +++ b/lib/cli.py @@ -79,6 +79,7 @@ __all__ = [ "FILESTORE_DRIVER_OPT", "FORCE_OPT", "FORCE_VARIANT_OPT", + "GATEWAY_OPT", "GLOBAL_FILEDIR_OPT", "HID_OS_OPT", "GLOBAL_SHARED_FILEDIR_OPT", @@ -101,6 +102,7 @@ __all__ = [ "MC_OPT", "MIGRATION_MODE_OPT", "NET_OPT", + "NETWORK_OPT", "NEW_CLUSTER_CERT_OPT", "NEW_CLUSTER_DOMAIN_SECRET_OPT", "NEW_CONFD_HMAC_KEY_OPT", @@ -143,6 +145,7 @@ __all__ = [ "REBOOT_TYPE_OPT", "REMOVE_INSTANCE_OPT", "REMOVE_UIDS_OPT", + "RESERVED_IPS_OPT", "RESERVED_LVS_OPT", "ROMAN_OPT", "SECONDARY_IP_OPT", @@ -195,11 +198,13 @@ __all__ = [ "ARGS_MANY_INSTANCES", "ARGS_MANY_NODES", "ARGS_MANY_GROUPS", + "ARGS_MANY_NETWORKS", "ARGS_NONE", "ARGS_ONE_INSTANCE", "ARGS_ONE_NODE", "ARGS_ONE_GROUP", "ARGS_ONE_OS", + "ARGS_ONE_NETWORK", "ArgChoice", "ArgCommand", "ArgFile", @@ -207,6 +212,7 @@ __all__ = [ "ArgHost", "ArgInstance", "ArgJobId", + "ArgNetwork", "ArgNode", "ArgOs", "ArgSuggest", @@ -217,6 +223,7 @@ __all__ = [ "OPT_COMPL_ONE_INSTANCE", "OPT_COMPL_ONE_NODE", "OPT_COMPL_ONE_NODEGROUP", + "OPT_COMPL_ONE_NETWORK", "OPT_COMPL_ONE_OS", "OPT_COMPL_NIC_PARAMS", "OPT_COMPL_DISK_PARAMS", @@ -302,6 +309,11 @@ class ArgNode(_Argument): """ +class ArgNetwork(_Argument): + """Network argument. + + """ + class ArgGroup(_Argument): """Node group argument. @@ -340,9 +352,11 @@ class ArgOs(_Argument): ARGS_NONE = [] ARGS_MANY_INSTANCES = [ArgInstance()] +ARGS_MANY_NETWORKS = [ArgNetwork()] ARGS_MANY_NODES = [ArgNode()] ARGS_MANY_GROUPS = [ArgGroup()] ARGS_ONE_INSTANCE = [ArgInstance(min=1, max=1)] +ARGS_ONE_NETWORK = [ArgNetwork(min=1, max=1)] ARGS_ONE_NODE = [ArgNode(min=1, max=1)] # TODO ARGS_ONE_GROUP = [ArgGroup(min=1, max=1)] @@ -559,9 +573,10 @@ def check_bool(option, opt, value): # pylint: disable-msg=W0613 OPT_COMPL_ONE_IALLOCATOR, OPT_COMPL_INST_ADD_NODES, OPT_COMPL_ONE_NODEGROUP, + OPT_COMPL_ONE_NETWORK, OPT_COMPL_NIC_PARAMS, OPT_COMPL_DISK_PARAMS, - OPT_COMPL_BACKEND_PARAMS) = range(100, 110) + OPT_COMPL_BACKEND_PARAMS) = range(100, 111) OPT_COMPL_ALL = frozenset([ OPT_COMPL_MANY_NODES, @@ -571,6 +586,7 @@ OPT_COMPL_ALL = frozenset([ OPT_COMPL_ONE_IALLOCATOR, OPT_COMPL_INST_ADD_NODES, OPT_COMPL_ONE_NODEGROUP, + OPT_COMPL_ONE_NETWORK, OPT_COMPL_NIC_PARAMS, OPT_COMPL_DISK_PARAMS, OPT_COMPL_BACKEND_PARAMS, @@ -1185,6 +1201,18 @@ NODE_POWERED_OPT = cli_option("--node-powered", default=None, dest="node_powered", help="Specify if the SoR for node is powered") +NETWORK_OPT = cli_option("--network", + action="store", default=None, dest="network", + help="IP network in CIDR notation") + +GATEWAY_OPT = cli_option("--gateway", + action="store", default=None, dest="gateway", + help="IP address of the router (gateway)") + +RESERVED_IPS_OPT = cli_option("--reserved-ips", + action="store", default=None, dest="reserved_ips", + help="Comma-delimited list of reserved IPs") + #: Options provided by all commands COMMON_OPTS = [DEBUG_OPT] diff --git a/lib/client/gnt_network.py b/lib/client/gnt_network.py new file mode 100644 index 000000000..65a3d02e4 --- /dev/null +++ b/lib/client/gnt_network.py @@ -0,0 +1,274 @@ +# +# + +# Copyright (C) 2011 Google Inc. +# +# 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; either version 2 of the License, or +# (at your option) any later version. +# +# 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. + +"""IP pool related commands""" + +# pylint: disable-msg=W0401,W0614 +# W0401: Wildcard import ganeti.cli +# W0614: Unused import %s from wildcard import (since we need cli) + +from ganeti.cli import * +from ganeti import constants +from ganeti import opcodes +from ganeti import utils +from textwrap import wrap + + +#: default list of fields for L{ListNetworks} +_LIST_DEF_FIELDS = ["name", "network", "gateway", "group_cnt", "group_links"] + + +def AddNetwork(opts, args): + """Add a network to the cluster. + + @param opts: the command line options selected by the user + @type args: list + @param args: a list of length 1 with the link name the pool to create + @rtype: int + @return: the desired exit code + + """ + (network_name, ) = args + if opts.reserved_ips is not None: + if opts.reserved_ips == "": + opts.reserved_ips = [] + else: + opts.reserved_ips = utils.UnescapeAndSplit(opts.reserved_ips, sep=",") + + op = opcodes.OpNetworkAdd(network_name=network_name, + gateway=opts.gateway, + network=opts.network, + reserved_ips=opts.reserved_ips) + SubmitOpCode(op, opts=opts) + + +def MapNetwork(opts, args): + """Map a network to a node group. + + @param opts: the command line options selected by the user + @type args: list + @param args: a list of length 3 with the link name the pool to create + @rtype: int + @return: the desired exit code + + """ + op = opcodes.OpGroupSetParams(group_name=args[1], + network=[constants.DDM_ADD, + args[0], args[2]]) + SubmitOpCode(op, opts=opts) + + +def UnmapNetwork(opts, args): + """Unmap a network from a node group. + + @param opts: the command line options selected by the user + @type args: list + @param args: a list of length 3 with the link name the pool to create + @rtype: int + @return: the desired exit code + + """ + op = opcodes.OpGroupSetParams(group_name=args[1], + network=[constants.DDM_REMOVE, + args[0], None]) + SubmitOpCode(op, opts=opts) + +def ListNetworks(opts, args): + """List Ip pools and their properties. + + @param opts: the command line options selected by the user + @type args: list + @param args: networks to list, or empty for all + @rtype: int + @return: the desired exit code + + """ + desired_fields = ParseFields(opts.output, _LIST_DEF_FIELDS) + fmtoverride = { + "group_links": (",".join, False), + "inst_list": (",".join, False), + } + + return GenericList(constants.QR_NETWORK, desired_fields, args, None, + opts.separator, not opts.no_headers, + verbose=opts.verbose, format_override=fmtoverride) + + +def ListNetworkFields(opts, args): + """List network fields. + + @param opts: the command line options selected by the user + @type args: list + @param args: fields to list, or empty for all + @rtype: int + @return: the desired exit code + + """ + return GenericListFields(constants.QR_NETWORK, args, opts.separator, + not opts.no_headers) + + +def ShowNetworkConfig(opts, args): + """Show network information. + + @param opts: the command line options selected by the user + @type args: list + @param args: should either be an empty list, in which case + we show information about all nodes, or should contain + a list of networks (names or UUIDs) to be queried for information + @rtype: int + @return: the desired exit code + + """ + cl = GetClient() + result = cl.QueryNetworks(fields=["name", "network", "gateway", + "free_count", "reserved_count", + "map", "group_links", "inst_list"], + names=args, use_locking=False) + + for (name, network, gateway, free_count, reserved_count, + map, group_links, instances) in result: + size = free_count + reserved_count + ToStdout("Network name: %s", name) + ToStdout(" subnet: %s", network) + ToStdout(" gateway: %s", gateway) + ToStdout(" size: %d", size) + ToStdout(" free: %d (%.2f%%)", free_count, + 100 * float(free_count)/float(size)) + ToStdout(" usage map:") + idx = 0 + for line in wrap(map, width=64): + ToStdout(" %s %s %d", str(idx).rjust(3), line.ljust(64), idx + 63) + idx += 64 + ToStdout(" (X) used (.) free") + + if group_links: + ToStdout(" connected to node groups:") + for conn in group_links: + group, link = conn.split(":") + ToStdout(" %s (link %s)", group, link) + else: + ToStdout(" not connected to any node group") + + if instances: + ToStdout(" used by %d instances:", len(instances)) + for inst in instances: + ToStdout(" %s", inst) + else: + ToStdOut(" not used by any instances") + + +def SetNetworkParams(opts, args): + """Modifies an IP address pool's parameters. + + @param opts: the command line options selected by the user + @type args: list + @param args: should contain only one element, the node group name + + @rtype: int + @return: the desired exit code + + """ + all_changes = { + "gateway": opts.gateway, + "reserved_ips": opts.reserved_ips, + } + + if all_changes.values().count(None) == len(all_changes): + ToStderr("Please give at least one of the parameters.") + return 1 + + op = opcodes.OpNetworkSetParams(network_name=args[0], + # pylint: disable-msg=W0142 + **all_changes) + result = SubmitOrSend(op, opts) + + if result: + ToStdout("Modified ip pool %s", args[0]) + for param, data in result: + ToStdout(" - %-5s -> %s", param, data) + + return 0 + + +def RemoveNetwork(opts, args): + """Remove an IP address pool from the cluster. + + @param opts: the command line options selected by the user + @type args: list + @param args: a list of length 1 with the id of the IP address pool to remove + @rtype: int + @return: the desired exit code + + """ + (network_name,) = args + op = opcodes.OpNetworkRemove(network_name=network_name, force=opts.force) + SubmitOpCode(op, opts=opts) + + +commands = { + "add": ( + AddNetwork, ARGS_ONE_NETWORK, + [DRY_RUN_OPT, NETWORK_OPT, GATEWAY_OPT, RESERVED_IPS_OPT], + "<network_name>", "Add a new IP network to the cluster"), + "list": ( + ListNetworks, ARGS_MANY_NETWORKS, + [NOHDR_OPT, SEP_OPT, FIELDS_OPT, VERBOSE_OPT], + "[<network_id>...]", + "Lists the IP networks in the cluster. The available fields can be shown" + " using the \"list-fields\" command (see the man page for details)." + " The default list is (in order): %s." % utils.CommaJoin(_LIST_DEF_FIELDS)), + "list-fields": ( + ListNetworkFields, [ArgUnknown()], [NOHDR_OPT, SEP_OPT], "[fields...]", + "Lists all available fields for networks"), + # + #"list-maps" + # + #"info" + "info": ( + ShowNetworkConfig, ARGS_MANY_NETWORKS, [], + "[<network_name>...]", "Show information about the network(s)"), + # + #"modify": ( + # SetNetworkParams, ARGS_ONE_NETWORK, + # [DRY_RUN_OPT, SUBMIT_OPT, RESERVED_IPS_OPT, NETWORK_OPT, GATEWAY_OPT], + # "<network_name>", "Alters the parameters of a network"), + "connect": ( + MapNetwork, + [ArgNetwork(min=1, max=1), ArgGroup(min=1, max=1), + ArgUnknown(min=1, max=1)], + [], + "<network_name> <node_group> <link_name>", + "Map a given network to a link of a specified node group"), + "disconnect": ( + UnmapNetwork, + [ArgNetwork(min=1, max=1), ArgGroup(min=1, max=1)], + [], + "<network_name> <node_group>", + "Unmap a given network from a specified node group"), + #"remove": ( + # RemoveNetwork, ARGS_ONE_NETWORK, [FORCE_OPT, DRY_RUN_OPT], + # "[--dry-run] <network_id>", + # "Remove an (empty) network from the cluster"), +} + + +def Main(): + return GenericMain(commands) diff --git a/man/gnt-network.rst b/man/gnt-network.rst new file mode 100644 index 000000000..831ccbde1 --- /dev/null +++ b/man/gnt-network.rst @@ -0,0 +1,154 @@ +gnt-network(8) Ganeti | Version @GANETI_VERSION@ +================================================ + +Name +---- + +gnt-network - Ganeti network administration + +Synopsis +-------- + +**gnt-network** {command} [arguments...] + +DESCRIPTION +----------- + +The **gnt-network** command is used for network definition administration +in the Ganeti system. + +COMMANDS +-------- + +ADD +~~~ + +| **add** +| [--network=*NETWORK*] +| [--gateway=*GATEWAY*] +| {*network*} + +Creates a new network with the given name. The network will be unused +initially. To connect it to a node group, use ``gnt-network connect``. + +The ``--network`` option allows you to specify the network in a CIDR notation. + +The ``--gateway`` option allows you to specify the default gateway for this +network. + +MODIFY +~~~~~~ + +| **modify** +| [--node-parameters=*NDPARAMS*] +| [--alloc-policy=*POLICY*] +| {*group*} + +Modifies some parameters from the node group. + +The ``--node-parameters`` and ``--alloc-policy`` optiosn are documented +in the **add** command above. + +REMOVE +~~~~~~ + +| **remove** {*group*} + +Deletes the indicated node group, which must be empty. There must always be at +least one group, so the last group cannot be removed. + +LIST +~~~~ + +| **list** [--no-headers] [--separator=*SEPARATOR*] [-v] +| [-o *[+]FIELD,...*] [network...] + +Lists all existing node groups in the cluster. + +The ``--no-headers`` option will skip the initial header line. The +``--separator`` option takes an argument which denotes what will be +used between the output fields. Both these options are to help +scripting. + +The ``-v`` option activates verbose mode, which changes the display of +special field states (see **ganeti(7)**). + +The ``-o`` option takes a comma-separated list of output fields. +If the value of the option starts with the character ``+``, the new +fields will be added to the default list. This allows to quickly +see the default list plus a few other fields, instead of retyping +the entire list of fields. + +The available fields and their meaning are: + +name + the group name + +uuid + the group's UUID + +node_cnt + the number of nodes in the node group + +node_list + the list of nodes that belong to this group + +pinst_cnt + the number of primary instances in the group (i.e., the number of + primary instances nodes in this group have) + +pinst_list + the list of primary instances in the group + +alloc_policy + the current allocation policy for the group + +ctime + the creation time of the group; note that this field contains spaces + and as such it's harder to parse + + if this attribute is not present (e.g. when upgrading from older + versions), then "N/A" will be shown instead + +mtime + the last modification time of the group; note that this field + contains spaces and as such it's harder to parse + +serial_no + the so called 'serial number' of the group; this is a numeric field + that is incremented each time the node is modified, and it can be + used to detect modifications + +If no group names are given, then all groups are included. Otherwise, +only the named groups will be listed. + +LIST-FIELDS +~~~~~~~~~~~ + +**list-fields** [field...] + +List available fields for node groups. + +RENAME +~~~~~~ + +| **rename** {*oldname*} {*newname*} + +Renames a given group from *oldname* to *newname*. + +INFO +~~~~ + +| **info** [network...] + +Displays information about a given network. + +CONNECT +~~~~~~~ +| **connect** {*network*} {*group*} {*link*} + +Connect a network to a given nodegroup's link. + +DISCONNECT +~~~~~~~~~~ +| **disconnect** {*network*} {*group*} -- GitLab