diff --git a/doc/hooks.rst b/doc/hooks.rst index be4c730b5b0d4e927cdc998b575847b07736f902..956ad303616537b29634a25338f0b7bb0ab808cd 100644 --- a/doc/hooks.rst +++ b/doc/hooks.rst @@ -214,6 +214,69 @@ Evacuates a node group. :pre-execution: master node and all nodes in the group :post-execution: master node and all nodes in the group +Network operations +~~~~~~~~~~~~~~~~~~ + +OP_NETWORK_ADD +++++++++++++++ + +Adds a network to the cluster. + +:directory: network-add +:env. vars: NETWORK_NAME, NETWORK_SUBNET, NETWORK_GATEWAY, NETWORK_SUBNET6, + NETWORK_GATEWAY6, NETWORK_TYPE, NETWORK_MAC_PREFIX, NETWORK_TAGS +:pre-execution: master node +:post-execution: master node + +OP_NETWORK_REMOVE ++++++++++++++++++ + +Removes a network from the cluster. + +:directory: network-remove +:env. vars: NETWORK_NAME +:pre-execution: master node +:post-execution: master node + +OP_NETWORK_CONNECT +++++++++++++++++++ + +Connects a network to a nodegroup. + +:directory: network-connect +:env. vars: GROUP_NAME, NETWORK_NAME, + GROUP_NETWORK_MODE, GROUP_NETWORK_LINK, + NETWORK_SUBNET, NETWORK_GATEWAY, NETWORK_SUBNET6, + NETWORK_GATEWAY6, NETWORK_TYPE, NETWORK_MAC_PREFIX, NETWORK_TAGS +:pre-execution: nodegroup nodes +:post-execution: nodegroup nodes + + +OP_NETWORK_DISCONNECT ++++++++++++++++++++++ + +Disconnects a network from a nodegroup. + +:directory: network-disconnect +:env. vars: GROUP_NAME, NETWORK_NAME, + GROUP_NETWORK_MODE, GROUP_NETWORK_LINK, + NETWORK_SUBNET, NETWORK_GATEWAY, NETWORK_SUBNET6, + NETWORK_GATEWAY6, NETWORK_TYPE, NETWORK_MAC_PREFIX, NETWORK_TAGS +:pre-execution: nodegroup nodes +:post-execution: nodegroup nodes + + +OP_NETWORK_SET_PARAMS ++++++++++++++++++++++ + +Modifies a network. + +:directory: network-modify +:env. vars: NETWORK_NAME, NETWORK_SUBNET, NETWORK_GATEWAY, NETWORK_SUBNET6, + NETWORK_GATEWAY6, NETWORK_TYPE, NETWORK_MAC_PREFIX, NETWORK_TAGS +:pre-execution: master node +:post-execution: master node + Instance operations ~~~~~~~~~~~~~~~~~~~ diff --git a/doc/rapi.rst b/doc/rapi.rst index 179edfd7fd1d26129a6049107a0cc2baed257ac0..e8c391cfa7722ed5ef198025f9e6cbc4f0ace1ac 100644 --- a/doc/rapi.rst +++ b/doc/rapi.rst @@ -646,6 +646,207 @@ to URI like:: It supports the ``dry-run`` argument. +``/2/networks`` ++++++++++++++++ + +The networks resource. + +It supports the following commands: ``GET``, ``POST``. + +``GET`` +~~~~~~~ + +Returns a list of all existing networks. + +Example:: + + [ + { + "name": "network1", + "uri": "\/2\/networks\/network1" + }, + { + "name": "network2", + "uri": "\/2\/networks\/network2" + } + ] + +If the optional bool *bulk* argument is provided and set to a true value +(i.e ``?bulk=1``), the output contains detailed information about networks +as a list. + +Returned fields: :pyeval:`utils.CommaJoin(sorted(rlib2.NET_FIELDS))`. + +Example:: + + [ + { + 'external_reservations': '10.0.0.0, 10.0.0.1, 10.0.0.15', + 'free_count': 13, + 'gateway': '10.0.0.1', + 'gateway6': None, + 'group_list': ['default(bridged, prv0)'], + 'inst_list': [], + 'mac_prefix': None, + 'map': 'XX.............X', + 'name': 'nat', + 'network': '10.0.0.0/28', + 'network6': None, + 'network_type': 'private', + 'reserved_count': 3, + 'tags': ['nfdhcpd'] + }, + ] + +``POST`` +~~~~~~~~ + +Creates a network. + +If the optional bool *dry-run* argument is provided, the job will not be +actually executed, only the pre-execution checks will be done. + +Returns: a job ID that can be used later for polling. + +Body parameters: + +.. opcode_params:: OP_NETWORK_ADD + +Job result: + +.. opcode_result:: OP_NETWORK_ADD + + +``/2/networks/[network_name]`` +++++++++++++++++++++++++++++++ + +Returns information about a network. + +It supports the following commands: ``GET``, ``DELETE``. + +``GET`` +~~~~~~~ + +Returns information about a network, similar to the bulk output from +the network list. + +Returned fields: :pyeval:`utils.CommaJoin(sorted(rlib2.NET_FIELDS))`. + +``DELETE`` +~~~~~~~~~~ + +Deletes a network. + +It supports the ``dry-run`` argument. + +Job result: + +.. opcode_result:: OP_NETWORK_REMOVE + + +``/2/networks/[network_name]/modify`` ++++++++++++++++++++++++++++++++++++++ + +Modifies the parameters of a network. + +Supports the following commands: ``PUT``. + +``PUT`` +~~~~~~~ + +Returns a job ID. + +Body parameters: + +.. opcode_params:: OP_NETWORK_SET_PARAMS + +Job result: + +.. opcode_result:: OP_NETWORK_SET_PARAMS + + +``/2/networks/[network_name]/connect`` +++++++++++++++++++++++++++++++++++++++ + +Connects a network to a nodegroup. + +Supports the following commands: ``PUT``. + +``PUT`` +~~~~~~~ + +Returns a job ID. It supports the ``dry-run`` arguments. + +Body parameters: + +.. opcode_params:: OP_NETWORK_CONNECT + +Job result: + +.. opcode_result:: OP_NETWORK_CONNECT + + +``/2/networks/[network_name]/disconnect`` ++++++++++++++++++++++++++++++++++++++++++ + +Disonnects a network from a nodegroup. + +Supports the following commands: ``PUT``. + +``PUT`` +~~~~~~~ + +Returns a job ID. It supports the ``dry-run`` arguments. + +Body parameters: + +.. opcode_params:: OP_NETWORK_DISCONNECT + +Job result: + +.. opcode_result:: OP_NETWORK_DISCONNECT + + +``/2/networks/[network_name]/tags`` ++++++++++++++++++++++++++++++++++++ + +Manages per-network tags. + +Supports the following commands: ``GET``, ``PUT``, ``DELETE``. + +``GET`` +~~~~~~~ + +Returns a list of tags. + +Example:: + + ["tag1", "tag2", "tag3"] + +``PUT`` +~~~~~~~ + +Add a set of tags. + +The request as a list of strings should be ``PUT`` to this URI. The +result will be a job id. + +It supports the ``dry-run`` argument. + + +``DELETE`` +~~~~~~~~~~ + +Delete a tag. + +In order to delete a set of tags, the DELETE request should be addressed +to URI like:: + + /tags?tag=[tag]&tag=[tag] + +It supports the ``dry-run`` argument. + + ``/2/instances-multi-alloc`` ++++++++++++++++++++++++++++ diff --git a/lib/network.py b/lib/network.py index 747904083805f29b0b2fcbe22ce197e620a3e255..96ed654ad7ea942b95b510ac94f0a480d2bcaf82 100644 --- a/lib/network.py +++ b/lib/network.py @@ -115,7 +115,8 @@ class AddressPool(object): assert self.net.family == 4 assert len(self.reservations) == self._GetSize() assert len(self.ext_reservations) == self._GetSize() - assert not (self.reservations & self.ext_reservations).any() + all_res = self.reservations & self.ext_reservations + assert not all_res.any() if self.gateway is not None: assert self.net.family == self.gateway.version diff --git a/lib/opcodes.py b/lib/opcodes.py index 9436935c7116285c8a22628605fd229d0a84e848..707d3389d2c006b95ed80739d6e12f6d3206ba3c 100644 --- a/lib/opcodes.py +++ b/lib/opcodes.py @@ -2011,15 +2011,22 @@ class OpNetworkAdd(OpCode): OP_PARAMS = [ _PNetworkName, _PNetworkType, - ("network", None, ht.TAnd(ht.TString ,_CheckCIDRNetNotation), None), - ("gateway", None, ht.TOr(ht.TNone, _CheckCIDRAddrNotation), None), - ("network6", None, ht.TOr(ht.TNone, _CheckCIDR6NetNotation), None), - ("gateway6", None, ht.TOr(ht.TNone, _CheckCIDR6AddrNotation), None), - ("mac_prefix", None, ht.TMaybeString, None), + ("network", None, ht.TAnd(ht.TString ,_CheckCIDRNetNotation), + "IPv4 Subnet"), + ("gateway", None, ht.TOr(ht.TNone, _CheckCIDRAddrNotation), + "IPv4 Gateway"), + ("network6", None, ht.TOr(ht.TNone, _CheckCIDR6NetNotation), + "IPv6 Subnet"), + ("gateway6", None, ht.TOr(ht.TNone, _CheckCIDR6AddrNotation), + "IPv6 Gateway"), + ("mac_prefix", None, ht.TMaybeString, + "Mac prefix that overrides cluster one"), ("add_reserved_ips", None, - ht.TOr(ht.TNone, ht.TListOf(_CheckCIDRAddrNotation)), None), + ht.TOr(ht.TNone, ht.TListOf(_CheckCIDRAddrNotation)), + "Which IPs to reserve"), ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Network tags"), ] + OP_RESULT = ht.TNone class OpNetworkRemove(OpCode): """Remove an existing network from the cluster. @@ -2031,6 +2038,7 @@ class OpNetworkRemove(OpCode): _PNetworkName, _PForce, ] + OP_RESULT = ht.TNone class OpNetworkSetParams(OpCode): """Modify Network's parameters except for IPv4 subnet""" @@ -2038,15 +2046,22 @@ class OpNetworkSetParams(OpCode): OP_PARAMS = [ _PNetworkName, _PNetworkType, - ("gateway", None, ht.TOr(ht.TNone, _CheckCIDRAddrNotation), None), - ("network6", None, ht.TOr(ht.TNone, _CheckCIDR6NetNotation), None), - ("gateway6", None, ht.TOr(ht.TNone, _CheckCIDR6AddrNotation), None), - ("mac_prefix", None, ht.TMaybeString, None), + ("gateway", None, ht.TOr(ht.TNone, _CheckCIDRAddrNotation), + "IPv4 Gateway"), + ("network6", None, ht.TOr(ht.TNone, _CheckCIDR6NetNotation), + "IPv6 Subnet"), + ("gateway6", None, ht.TOr(ht.TNone, _CheckCIDR6AddrNotation), + "IPv6 Gateway"), + ("mac_prefix", None, ht.TMaybeString, + "Mac prefix that overrides cluster one"), ("add_reserved_ips", None, - ht.TOr(ht.TNone, ht.TListOf(_CheckCIDRAddrNotation)), None), + ht.TOr(ht.TNone, ht.TListOf(_CheckCIDRAddrNotation)), + "Which external IPs to reserve"), ("remove_reserved_ips", None, - ht.TOr(ht.TNone, ht.TListOf(_CheckCIDRAddrNotation)), None), + ht.TOr(ht.TNone, ht.TListOf(_CheckCIDRAddrNotation)), + "Which external IPs to release"), ] + OP_RESULT = ht.TNone class OpNetworkConnect(OpCode): """Connect a Network to a specific Nodegroup with the defined netparams @@ -2060,10 +2075,11 @@ class OpNetworkConnect(OpCode): OP_PARAMS = [ _PGroupName, _PNetworkName, - ("network_mode", None, ht.TString, None), - ("network_link", None, ht.TString, None), - ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"), + ("network_mode", None, ht.TString, "Connectivity mode"), + ("network_link", None, ht.TString, "Connectivity link"), + ("conflicts_check", True, ht.TBool, "Whether to check for conflicting IPs"), ] + OP_RESULT = ht.TNone class OpNetworkDisconnect(OpCode): """Disconnect a Network from a Nodegroup. Produce errors if NICs are @@ -2074,8 +2090,9 @@ class OpNetworkDisconnect(OpCode): OP_PARAMS = [ _PGroupName, _PNetworkName, - ("conflicts_check", True, ht.TBool, "Check for conflicting IPs"), + ("conflicts_check", True, ht.TBool, "Whether to check for conflicting IPs"), ] + OP_RESULT = ht.TNone class OpNetworkQuery(OpCode): """Compute the list of networks.""" @@ -2084,6 +2101,7 @@ class OpNetworkQuery(OpCode): ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Empty list to query all groups, group names otherwise"), ] + OP_RESULT = ht.TNone def _GetOpList(): diff --git a/lib/ovf.py b/lib/ovf.py index dc581331cca87a03776e5ca8a418c97d86c11684..557b3a3126fcab43a0882f47c161ddf14845b8cc 100644 --- a/lib/ovf.py +++ b/lib/ovf.py @@ -760,7 +760,7 @@ class OVFWriter(object): SubElementText(nic, "gnt:MACAddress", network["mac"]) SubElementText(nic, "gnt:IPAddress", network["ip"]) SubElementText(nic, "gnt:Link", network["link"]) - SubElementText(nic, "gnt:Network", network["network"]) + SubElementText(nic, "gnt:Net", network["network"]) def SaveVirtualSystemData(self, name, vcpus, memory): """Convert virtual system information to OVF sections. @@ -1665,7 +1665,8 @@ class OVFExporter(Converter): counter = 0 while True: data_link = \ - self.config_parser.get(constants.INISECT_INS, "nic%s_network" % counter) + self.config_parser.get(constants.INISECT_INS, + "nic%s_link" % counter) if data_link is None: break results.append({ @@ -1675,9 +1676,9 @@ class OVFExporter(Converter): "nic%s_mac" % counter), "ip": self.config_parser.get(constants.INISECT_INS, "nic%s_ip" % counter), - "link": self.config_parser.get(constants.INISECT_INS, - "nic%s_link" % counter), - "network": data_link, + "network": self.config_parser.get(constants.INISECT_INS, + "nic%s_network" % counter), + "link": data_link, }) if results[counter]["mode"] not in constants.NIC_VALID_MODES: raise errors.OpPrereqError("Network mode %s not recognized" diff --git a/lib/rapi/client.py b/lib/rapi/client.py index a1d729fcd20a7e3a342b5e273e4dc9cc3857197c..ca2a965a499899aeb1ad95d414466a10fec12baf 100644 --- a/lib/rapi/client.py +++ b/lib/rapi/client.py @@ -1761,7 +1761,7 @@ class GanetiRapiClient(object): # pylint: disable=R0904 return self._SendRequest(HTTP_POST, "/%s/networks" % GANETI_RAPI_VERSION, query, body) - def ConnectNetwork(self, network_name, group_name, mode, link): + def ConnectNetwork(self, network_name, group_name, mode, link, dry_run=False): """Connects a Network to a NodeGroup with the given netparams """ @@ -1771,22 +1771,44 @@ class GanetiRapiClient(object): # pylint: disable=R0904 "network_link": link } + query = [] + _AppendDryRunIf(query, dry_run) + return self._SendRequest(HTTP_PUT, ("/%s/networks/%s/connect" % - (GANETI_RAPI_VERSION, network_name)), None, body) + (GANETI_RAPI_VERSION, network_name)), query, body) - def DisconnectNetwork(self, network_name, group_name): + def DisconnectNetwork(self, network_name, group_name, dry_run=False): """Connects a Network to a NodeGroup with the given netparams """ body = { "group_name": group_name } + + query = [] + _AppendDryRunIf(query, dry_run) + return self._SendRequest(HTTP_PUT, ("/%s/networks/%s/disconnect" % - (GANETI_RAPI_VERSION, network_name)), None, body) + (GANETI_RAPI_VERSION, network_name)), query, body) + def ModifyNetwork(self, network, **kwargs): + """Modifies a network. + + More details for parameters can be found in the RAPI documentation. + + @type network: string + @param network: Network name + @rtype: string + @return: job id + + """ + return self._SendRequest(HTTP_PUT, + ("/%s/networks/%s/modify" % + (GANETI_RAPI_VERSION, network)), None, kwargs) + def DeleteNetwork(self, network, dry_run=False): """Deletes a network. @@ -1806,6 +1828,62 @@ class GanetiRapiClient(object): # pylint: disable=R0904 ("/%s/networks/%s" % (GANETI_RAPI_VERSION, network)), query, None) + def GetNetworkTags(self, network): + """Gets tags for a network. + + @type network: string + @param network: Node group whose tags to return + + @rtype: list of strings + @return: tags for the network + + """ + return self._SendRequest(HTTP_GET, + ("/%s/networks/%s/tags" % + (GANETI_RAPI_VERSION, network)), None, None) + + def AddNetworkTags(self, network, tags, dry_run=False): + """Adds tags to a network. + + @type network: str + @param network: network to add tags to + @type tags: list of string + @param tags: tags to add to the network + @type dry_run: bool + @param dry_run: whether to perform a dry run + + @rtype: string + @return: job id + + """ + query = [("tag", t) for t in tags] + _AppendDryRunIf(query, dry_run) + + return self._SendRequest(HTTP_PUT, + ("/%s/networks/%s/tags" % + (GANETI_RAPI_VERSION, network)), query, None) + + def DeleteNetworkTags(self, network, tags, dry_run=False): + """Deletes tags from a network. + + @type network: str + @param network: network to delete tags from + @type tags: list of string + @param tags: tags to delete + @type dry_run: bool + @param dry_run: whether to perform a dry run + @rtype: string + @return: job id + + """ + query = [("tag", t) for t in tags] + _AppendDryRunIf(query, dry_run) + + return self._SendRequest(HTTP_DELETE, + ("/%s/networks/%s/tags" % + (GANETI_RAPI_VERSION, network)), query, None) + + def GetGroups(self, bulk=False): """Gets all node groups in the cluster. diff --git a/lib/rapi/connector.py b/lib/rapi/connector.py index 56a234620c7e0364702318d4628fbee9c670b16a..0192e7992d70ed7d24b8ffe6632168c7db95ed59 100644 --- a/lib/rapi/connector.py +++ b/lib/rapi/connector.py @@ -175,6 +175,10 @@ def GetHandlers(node_name_pattern, instance_name_pattern, rlib2.R_2_networks_name_connect, re.compile(r"^/2/networks/(%s)/disconnect$" % network_name_pattern): rlib2.R_2_networks_name_disconnect, + re.compile(r"^/2/networks/(%s)/modify$" % network_name_pattern): + rlib2.R_2_networks_name_modify, + re.compile(r"^/2/networks/(%s)/tags$" % network_name_pattern): + rlib2.R_2_networks_name_tags, "/2/groups": rlib2.R_2_groups, re.compile(r"^/2/groups/(%s)$" % group_name_pattern): diff --git a/lib/rapi/rlib2.py b/lib/rapi/rlib2.py index 57fe6313cea3e0def71387f3ae1ad85ecdc2060d..a18e12d75e5edc8c7ee769aee15924fa4c17b78a 100644 --- a/lib/rapi/rlib2.py +++ b/lib/rapi/rlib2.py @@ -688,7 +688,7 @@ class R_2_networks(baserlib.OpcodeResource): class R_2_networks_name(baserlib.OpcodeResource): - """/2/network/[network_name] resource. + """/2/networks/[network_name] resource. """ DELETE_OPCODE = opcodes.OpNetworkRemove @@ -718,7 +718,7 @@ class R_2_networks_name(baserlib.OpcodeResource): }) class R_2_networks_name_connect(baserlib.OpcodeResource): - """/2/network/[network_name]/connect. + """/2/networks/[network_name]/connect resource. """ PUT_OPCODE = opcodes.OpNetworkConnect @@ -730,10 +730,11 @@ class R_2_networks_name_connect(baserlib.OpcodeResource): assert self.items return (self.request_body, { "network_name": self.items[0], + "dry_run": self.dryRun(), }) class R_2_networks_name_disconnect(baserlib.OpcodeResource): - """/2/network/[network_name]/disconnect. + """/2/networks/[network_name]/disconnect resource. """ PUT_OPCODE = opcodes.OpNetworkDisconnect @@ -741,12 +742,29 @@ class R_2_networks_name_disconnect(baserlib.OpcodeResource): def GetPutOpInput(self): """Changes some parameters of node group. + """ + assert self.items + return (self.request_body, { + "network_name": self.items[0], + "dry_run": self.dryRun(), + }) + +class R_2_networks_name_modify(baserlib.OpcodeResource): + """/2/networks/[network_name]/modify resource. + + """ + PUT_OPCODE = opcodes.OpNetworkSetParams + + def GetPutOpInput(self): + """Changes some parameters of network. + """ assert self.items return (self.request_body, { "network_name": self.items[0], }) + class R_2_groups(baserlib.OpcodeResource): """/2/groups resource. @@ -1546,6 +1564,14 @@ class R_2_groups_name_tags(_R_Tags): """ TAG_LEVEL = constants.TAG_NODEGROUP +class R_2_networks_name_tags(_R_Tags): + """ /2/networks/[network_name]/tags resource. + + Manages per-network tags. + + """ + TAG_LEVEL = constants.TAG_NETWORK + class R_2_tags(_R_Tags): """ /2/tags resource. diff --git a/man/gnt-network.rst b/man/gnt-network.rst index 92640442f7e61257c5f550b0a49a02251acec852..6f75807a61bd325978240094d7cc61e92a785436 100644 --- a/man/gnt-network.rst +++ b/man/gnt-network.rst @@ -158,6 +158,45 @@ RENAME Renames a given network from *oldname* to *newname*. NOT implemeted yet +TAGS +~~~ + +ADD-TAGS +^^^^^^^^ + +**add-tags** [\--from *file*] {*networkname*} {*tag*...} + +Add tags to the given network. If any of the tags contains invalid +characters, the entire operation will abort. + +If the ``--from`` option is given, the list of tags will be extended +with the contents of that file (each line becomes a tag). In this case, +there is not need to pass tags on the command line (if you do, both +sources will be used). A file name of ``-`` will be interpreted as +stdin. + +LIST-TAGS +^^^^^^^^^ + +**list-tags** {*networkname*} + +List the tags of the given network. + +REMOVE-TAGS +^^^^^^^^^^^ + +**remove-tags** [\--from *file*] {*networkname*} {*tag*...} + +Remove tags from the given network. If any of the tags are not +existing on the network, the entire operation will abort. + +If the ``--from`` option is given, the list of tags to be removed will +be extended with the contents of that file (each line becomes a tag). In +this case, there is not need to pass tags on the command line (if you +do, tags from both sources will be removed). A file name of ``-`` will +be interpreted as stdin. + + INFO ~~~~ diff --git a/test/cfgupgrade_unittest.py b/test/cfgupgrade_unittest.py index 4d38b26756f83c56c77bd91facd095b10d2a2281..edfd11e35560a0fe19fea5db0f8f7ae560358b44 100755 --- a/test/cfgupgrade_unittest.py +++ b/test/cfgupgrade_unittest.py @@ -93,6 +93,7 @@ class TestCfgupgrade(unittest.TestCase): "version": constants.CONFIG_VERSION, "cluster": {}, "instances": {}, + "nodegroups": {}, })) hostname = netutils.GetHostname().name @@ -108,6 +109,7 @@ class TestCfgupgrade(unittest.TestCase): "version": constants.CONFIG_VERSION, "cluster": {}, "instances": {}, + "nodegroups": {}, })) utils.WriteFile(self.ss_master_node_path, @@ -124,6 +126,7 @@ class TestCfgupgrade(unittest.TestCase): "config_version": 0, }, "instances": {}, + "nodegroups": {}, } utils.WriteFile(self.config_path, data=serializer.DumpJson(cfg)) self.assertRaises(Exception, _RunUpgrade, self.tmpdir, False, True) @@ -148,6 +151,7 @@ class TestCfgupgrade(unittest.TestCase): "version": from_version, "cluster": cluster, "instances": {}, + "nodegroups": {}, } self._CreateValidConfigDir() utils.WriteFile(self.config_path, data=serializer.DumpJson(cfg)) diff --git a/test/data/ovfdata/config.ini b/test/data/ovfdata/config.ini index 7d0c0f581877bc51fc8762cd03d3299e6f0d922a..d5a0586ce80b966338b4a35bf7377d57a725e10e 100644 --- a/test/data/ovfdata/config.ini +++ b/test/data/ovfdata/config.ini @@ -8,6 +8,7 @@ nic0_mac = aa:00:00:d8:2c:1e nic_count = 1 nic0_link = br0 nic0_ip = None +nic0_network = test disk0_ivname = disk/0 disk0_size = 0 diff --git a/test/docs_unittest.py b/test/docs_unittest.py index 048c0fa8bc0bfb8ed42cbcc757ec28081e4ac2eb..1fa24f5514d6119b1b5ea5e2b201367067a2bfe6 100755 --- a/test/docs_unittest.py +++ b/test/docs_unittest.py @@ -186,11 +186,13 @@ class TestRapiDocs(unittest.TestCase): node_name = re.escape("[node_name]") instance_name = re.escape("[instance_name]") group_name = re.escape("[group_name]") + network_name = re.escape("[network_name]") job_id = re.escape("[job_id]") disk_index = re.escape("[disk_index]") query_res = re.escape("[resource]") - resources = connector.GetHandlers(node_name, instance_name, group_name, + resources = connector.GetHandlers(node_name, instance_name, + group_name, network_name, job_id, disk_index, query_res) handler_dups = utils.FindDuplicates(resources.values()) @@ -202,6 +204,7 @@ class TestRapiDocs(unittest.TestCase): re.compile(node_name): "node1examplecom", re.compile(instance_name): "inst1examplecom", re.compile(group_name): "group4440", + re.compile(network_name): "network5550", re.compile(job_id): "9409", re.compile(disk_index): "123", re.compile(query_res): "lock", diff --git a/test/ganeti.locking_unittest.py b/test/ganeti.locking_unittest.py index 6b4ad8d7149618f3f3b735fdf28510f018ef5210..85fa0fc66251016660a7332ea98153f7b71ff771 100755 --- a/test/ganeti.locking_unittest.py +++ b/test/ganeti.locking_unittest.py @@ -1762,8 +1762,9 @@ class TestGanetiLockManager(_ThreadedTestCase): self.nodes=['n1', 'n2'] self.nodegroups=['g1', 'g2'] self.instances=['i1', 'i2', 'i3'] + self.networks=['net1', 'net2', 'net3'] self.GL = locking.GanetiLockManager(self.nodes, self.nodegroups, - self.instances) + self.instances, self.networks) def tearDown(self): # Don't try this at home... @@ -1778,7 +1779,7 @@ class TestGanetiLockManager(_ThreadedTestCase): self.assertEqual(i, locking.LEVELS[i]) def testDoubleGLFails(self): - self.assertRaises(AssertionError, locking.GanetiLockManager, [], [], []) + self.assertRaises(AssertionError, locking.GanetiLockManager, [], [], [], []) def testLockNames(self): self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL'])) @@ -1787,31 +1788,44 @@ class TestGanetiLockManager(_ThreadedTestCase): set(self.nodegroups)) self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE), set(self.instances)) + self.assertEqual(self.GL._names(locking.LEVEL_NETWORK), + set(self.networks)) def testInitAndResources(self): locking.GanetiLockManager._instance = None - self.GL = locking.GanetiLockManager([], [], []) + self.GL = locking.GanetiLockManager([], [], [], []) self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL'])) self.assertEqual(self.GL._names(locking.LEVEL_NODE), set()) self.assertEqual(self.GL._names(locking.LEVEL_NODEGROUP), set()) self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE), set()) + self.assertEqual(self.GL._names(locking.LEVEL_NETWORK), set()) locking.GanetiLockManager._instance = None - self.GL = locking.GanetiLockManager(self.nodes, self.nodegroups, []) + self.GL = locking.GanetiLockManager(self.nodes, self.nodegroups, [], []) self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL'])) self.assertEqual(self.GL._names(locking.LEVEL_NODE), set(self.nodes)) self.assertEqual(self.GL._names(locking.LEVEL_NODEGROUP), set(self.nodegroups)) self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE), set()) + self.assertEqual(self.GL._names(locking.LEVEL_NETWORK), set()) locking.GanetiLockManager._instance = None - self.GL = locking.GanetiLockManager([], [], self.instances) + self.GL = locking.GanetiLockManager([], [], self.instances, []) self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL'])) self.assertEqual(self.GL._names(locking.LEVEL_NODE), set()) self.assertEqual(self.GL._names(locking.LEVEL_NODEGROUP), set()) self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE), set(self.instances)) + locking.GanetiLockManager._instance = None + self.GL = locking.GanetiLockManager([], [], [], self.networks) + self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL'])) + self.assertEqual(self.GL._names(locking.LEVEL_NODE), set()) + self.assertEqual(self.GL._names(locking.LEVEL_NODEGROUP), set()) + self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE), set()) + self.assertEqual(self.GL._names(locking.LEVEL_NETWORK), + set(self.networks)) + def testAcquireRelease(self): self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1) self.assertEquals(self.GL.list_owned(locking.LEVEL_CLUSTER), set(['BGL'])) diff --git a/test/ganeti.ovf_unittest.py b/test/ganeti.ovf_unittest.py old mode 100644 new mode 100755 index fa9197c0524ee620eb55df61b497e98574cb2c49..da7e92d8afe310c13e807eda9b36466c72a02280 --- a/test/ganeti.ovf_unittest.py +++ b/test/ganeti.ovf_unittest.py @@ -1,7 +1,7 @@ #!/usr/bin/python # -# Copyright (C) 2011 Google Inc. +# Copyright (C) 2011, 2012 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 @@ -59,6 +59,7 @@ GANETI_NETWORKS = { "nic0_ip": "none", "nic0_mac": "aa:00:00:d8:2c:1e", "nic0_link": "xen-br0", + "nic0_network": "auto", } GANETI_HYPERVISOR = { "hypervisor_name": "xen-pvm", @@ -91,6 +92,7 @@ VIRTUALBOX_NETWORKS = { "nic0_ip": "none", "nic0_link": "auto", "nic0_mac": "auto", + "nic0_network": "auto", } VIRTUALBOX_HYPERVISOR = {"hypervisor_name": "auto"} VIRTUALBOX_OS = {"os_name": None} @@ -130,6 +132,7 @@ CMDARGS_NETWORKS = { "nic0_ip": "none", "nic0_mac": "auto", "nic_count": "1", + "nic0_network": "auto", } CMDARGS_HYPERVISOR = { "hypervisor_name": "xen-pvm" @@ -207,7 +210,8 @@ EXP_DISKS_LIST = [ }, ] EXP_NETWORKS_LIST = [ - {"mac": "aa:00:00:d8:2c:1e", "ip":"None", "link":"br0","mode":"routed"}, + {"mac": "aa:00:00:d8:2c:1e", "ip":"None", "link":"br0", + "mode":"routed", "network": "test"}, ] EXP_PARTIAL_GANETI_DICT = { "hypervisor": {"name": "xen-kvm"}, @@ -263,8 +267,8 @@ EXPORT_GANETI_INCOMPLETE = ("<gnt:GanetiSection><gnt:Version>0</gnt:Version>" "Nic ovf:name=\"routed0\"><gnt:Mode>routed</gnt:" "Mode><gnt:MACAddress>aa:00:00:d8:2c:1e</gnt:" "MACAddress><gnt:IPAddress>None</gnt:IPAddress>" - "<gnt:Link>br0</gnt:Link></gnt:Nic></gnt:Network>" - "</gnt:GanetiSection>") + "<gnt:Link>br0</gnt:Link><gnt:Net>test</gnt:Net>" + "</gnt:Nic></gnt:Network></gnt:GanetiSection>") EXPORT_GANETI = ("<gnt:GanetiSection><gnt:Version>0</gnt:Version><gnt:" "AutoBalance>False</gnt:AutoBalance><gnt:OperatingSystem>" "<gnt:Name>lenny-image</gnt:Name><gnt:Parameters /></gnt:" @@ -274,7 +278,8 @@ EXPORT_GANETI = ("<gnt:GanetiSection><gnt:Version>0</gnt:Version><gnt:" "Hypervisor><gnt:Network><gnt:Nic ovf:name=\"routed0\"><gnt:" "Mode>routed</gnt:Mode><gnt:MACAddress>aa:00:00:d8:2c:1e</gnt:" "MACAddress><gnt:IPAddress>None</gnt:IPAddress><gnt:Link>br0" - "</gnt:Link></gnt:Nic></gnt:Network></gnt:GanetiSection>") + "</gnt:Link><gnt:Net>test</gnt:Net></gnt:Nic></gnt:Network>" + "</gnt:GanetiSection>") EXPORT_SYSTEM = ("<References><File ovf:compression=\"gzip\" ovf:href=\"new_" "disk.cow.gz\" ovf:id=\"file0\" ovf:size=\"203\" /><File ovf:" "href=\"new_disk.cow\" ovf:id=\"file1\" ovf:size=\"15\" />" diff --git a/test/ganeti.rapi.client_unittest.py b/test/ganeti.rapi.client_unittest.py index da8409393df9af86558ef75975677c821b37221a..e5b243682638a29c5f9710d4c2d3a2501c94d71f 100755 --- a/test/ganeti.rapi.client_unittest.py +++ b/test/ganeti.rapi.client_unittest.py @@ -1115,6 +1115,93 @@ class GanetiRapiClientTests(testutils.GanetiTestCase): self.assertDryRun() self.assertUseForce() + def testGetNetworksBulk(self): + networks = [{"name": "network1", + "uri": "/2/networks/network1", + "network": "192.168.0.0/24", + }, + {"name": "network2", + "uri": "/2/networks/network2", + "network": "192.168.0.0/24", + }, + ] + self.rapi.AddResponse(serializer.DumpJson(networks)) + + self.assertEqual(networks, self.client.GetNetworks(bulk=True)) + self.assertHandler(rlib2.R_2_networks) + self.assertBulk() + + def testGetNetwork(self): + network = {"ctime": None, + "name": "network1", + } + self.rapi.AddResponse(serializer.DumpJson(network)) + self.assertEqual({"ctime": None, "name": "network1"}, + self.client.GetNetwork("network1")) + self.assertHandler(rlib2.R_2_networks_name) + self.assertItems(["network1"]) + + def testCreateNetwork(self): + self.rapi.AddResponse("12345") + job_id = self.client.CreateNetwork("newnetwork", network="192.168.0.0/24", + dry_run=True) + self.assertEqual(job_id, 12345) + self.assertHandler(rlib2.R_2_networks) + self.assertDryRun() + + def testModifyNetwork(self): + self.rapi.AddResponse("12346") + job_id = self.client.ModifyNetwork("mynetwork", gateway="192.168.0.10", + dry_run=True) + self.assertEqual(job_id, 12346) + self.assertHandler(rlib2.R_2_networks_name_modify) + + def testDeleteNetwork(self): + self.rapi.AddResponse("12347") + job_id = self.client.DeleteNetwork("newnetwork", dry_run=True) + self.assertEqual(job_id, 12347) + self.assertHandler(rlib2.R_2_networks_name) + self.assertDryRun() + + def testConnectNetwork(self): + self.rapi.AddResponse("12348") + job_id = self.client.ConnectNetwork("mynetwork", "default", + "bridged", "br0", dry_run=True) + self.assertEqual(job_id, 12348) + self.assertHandler(rlib2.R_2_networks_name_connect) + self.assertDryRun() + + def testDisconnectNetwork(self): + self.rapi.AddResponse("12349") + job_id = self.client.DisconnectNetwork("mynetwork", "default", dry_run=True) + self.assertEqual(job_id, 12349) + self.assertHandler(rlib2.R_2_networks_name_disconnect) + self.assertDryRun() + + def testGetNetworkTags(self): + self.rapi.AddResponse("[]") + self.assertEqual([], self.client.GetNetworkTags("fooNetwork")) + self.assertHandler(rlib2.R_2_networks_name_tags) + self.assertItems(["fooNetwork"]) + + def testAddNetworkTags(self): + self.rapi.AddResponse("1234") + self.assertEqual(1234, + self.client.AddNetworkTags("fooNetwork", ["awesome"], dry_run=True)) + self.assertHandler(rlib2.R_2_networks_name_tags) + self.assertItems(["fooNetwork"]) + self.assertDryRun() + self.assertQuery("tag", ["awesome"]) + + def testDeleteNetworkTags(self): + self.rapi.AddResponse("25826") + self.assertEqual(25826, self.client.DeleteNetworkTags("foo", ["awesome"], + dry_run=True)) + self.assertHandler(rlib2.R_2_networks_name_tags) + self.assertItems(["foo"]) + self.assertDryRun() + self.assertQuery("tag", ["awesome"]) + def testModifyInstance(self): self.rapi.AddResponse("23681") job_id = self.client.ModifyInstance("inst7210", os_name="linux") diff --git a/tools/cfgupgrade b/tools/cfgupgrade index ac182212815aea5f6539d50bff12276da55830ad..a33558af651422c58f7e45dfbe78f9d76efbf040 100755 --- a/tools/cfgupgrade +++ b/tools/cfgupgrade @@ -104,7 +104,6 @@ def UpgradeNetworks(config_data): def UpgradeGroups(config_data): - nicparams = config_data["cluster"]["nicparams"]["default"] for group in config_data["nodegroups"].values(): networks = group.get("networks", None) if not networks: