diff --git a/ansible/doc/binding-strategy.rst b/ansible/doc/binding-strategy.rst new file mode 100644 index 0000000..2dac04e --- /dev/null +++ b/ansible/doc/binding-strategy.rst @@ -0,0 +1,51 @@ +====================== +Binding Strategy Guide +====================== + +Overview +======== + +In OVN, a logical port can be bind to a local OVS port on any +chassis/hypervisor, depending on the VM scheduler (e.g., ``nova-scheduler``). +The binding strategy potentially impacts the network performance. That is +binding all logical ports from a logical network on a single hypervisor performs +differently than distributing the ports on multiple hypervisors. + +The container-based ovn-scale-test deployment allows to configure the binding +strategy in creating and binding port rally task. + + +Binding Configuration +===================== + +Use ``networks_per_sandbox`` to control how logical networks and the logical +ports are bind to chassis. + +For example, given ``ovn_number_chassis: 10`` (10 emulated chassis) and +``network_number: 10`` (10 logical networks), the binding varies depending +on the value of ``networks_per_sandbox``. + +- ``networks_per_sandbox: "10"``: this is the default case. All networks will +be evenly distributed to all chassis. + +- ``networks_per_sandbox: "2"``: each chassis has ports belong to two logical +networks. In this case, the 10 logical networks are divided into 5 groups, say +[n0, n1], [n2, n3], [n4, n5], [n6, n7], [n8, n9]. Then ports in [n0, n1] are +bind to chassis 0 and 1, [n2, n3] to chassis 2 and 3, and so forth. As a +result, each chassis has two logical network as configured. + +- ``networks_per_sandbox: "1"``: each chassis has ports belong to only one +logical network. In this case, the 10 logical network will have a one-to-one +mapping to the 10 chassis. Note that this is the extreme case as opposite to +``networks_per_sandbox: "10"``. + + +Constraint +~~~~~~~~~ + +TBD + +Implementation Detail +===================== + +TBD diff --git a/ansible/etc/variables.yml b/ansible/etc/variables.yml index 65df128..ec9d5ad 100644 --- a/ansible/etc/variables.yml +++ b/ansible/etc/variables.yml @@ -37,6 +37,9 @@ network_start_cidr: "172.16.201.0/24" network_number: "5" ports_per_network: "5" +# from 0 to network_number, 0 means even distribution (default) +networks_per_sandbox: "0" + acls_per_port: "1" ######################## diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml index 95c6e54..caa552f 100644 --- a/ansible/group_vars/all.yml +++ b/ansible/group_vars/all.yml @@ -44,6 +44,9 @@ ports_per_network: "1" ports_created_batch_size: "1" networks_created_batch_size: "1" +# from 0 to network_number, 0 means even distribution (default) +networks_per_sandbox: "0" + acls_per_port: "1" ######################## diff --git a/ansible/roles/rally/templates/create_and_bind_ports.json.j2 b/ansible/roles/rally/templates/create_and_bind_ports.json.j2 index f40af68..7043599 100644 --- a/ansible/roles/rally/templates/create_and_bind_ports.json.j2 +++ b/ansible/roles/rally/templates/create_and_bind_ports.json.j2 @@ -12,6 +12,7 @@ "start_cidr": "{{ network_start_cidr }}", "physical_network": "providernet" }, + "networks_per_sandbox": {{ networks_per_sandbox }}, "port_create_args" : { "batch": {{ ports_created_batch_size }} }, diff --git a/ci/ansible/all.yml b/ci/ansible/all.yml index f92bcc4..750f837 100644 --- a/ci/ansible/all.yml +++ b/ci/ansible/all.yml @@ -41,6 +41,9 @@ ports_created_batch_size: "1" networks_created_batch_size: "1" acls_per_port: "5" +# from 0 to network_number, 0 means even distribution (default) +networks_per_sandbox: "0" + ################ # OVS Repository ################ diff --git a/rally_ovs/plugins/ovs/scenarios/ovn_network.py b/rally_ovs/plugins/ovs/scenarios/ovn_network.py index dd771e3..624fc0b 100644 --- a/rally_ovs/plugins/ovs/scenarios/ovn_network.py +++ b/rally_ovs/plugins/ovs/scenarios/ovn_network.py @@ -15,12 +15,104 @@ from rally.common import logging from rally_ovs.plugins.ovs.scenarios import ovn +from rally import exceptions from rally.task import scenario from rally.task import validation LOG = logging.getLogger(__name__) +class LogicalNetwork(): + def __init__(self): + self.sandboxes = [] + self.ports_per_network = 0 + self.lswitch = None + def set_lswitch(self, lswitch): + self.lswitch = lswitch + + def add_sandbox(self, sandbox): + self.sandboxes.append(sandbox) + + def get_lswitch(self): + return self.lswitch + + def get_sandboxes(self): + return self.sandboxes + + def get_ports_per_network(self): + return self.ports_per_network + + +def initialize_logical_networks(lswitches): + LOG.info("Initialize logical lswitches with %s" % lswitches) + logical_networks = [] + + for lswitch in lswitches: + logical_network = LogicalNetwork() + logical_network.set_lswitch(lswitch) + LOG.info("Logical network: %s" % logical_network.get_lswitch()) + logical_networks.append(logical_network) + + return logical_networks + + +def allocate_networks_on_sandboxes(logical_networks, sandboxes, networks_per_sandbox=0): + if networks_per_sandbox == 0: + for logical_network in logical_networks: + for sandbox in sandboxes: + logical_network.add_sandbox(sandbox) + else: + # Sanity check + num_networks = len(logical_networks) + + if (num_networks % networks_per_sandbox) != 0 : + message = ("Number of network %s is not divisible by network per sandbox %s") + raise exceptions.InvalidConfigException( + message % (str(num_networks), str(networks_per_sandbox))) + + LOG.info("Number of networks: %s" % str(num_networks)) + num_network_groups = num_networks / networks_per_sandbox + LOG.info("Number of network group: %s" % str(num_network_groups)) + + if len(sandboxes) < num_network_groups : + message = ("Number of sandbox %d is less than number of network groups %d") + raise exceptions.InvalidConfigException( + message % (len(sandboxes), num_network_groups)) + elif (len(sandboxes) % num_network_groups) != 0 : + message = ("Number of sandbox %d is not divisible by network groups %d") + raise exceptions.InvalidConfigException( + message % (len(sandboxes), num_network_groups)) + + + group_spread_sandboxes = len(sandboxes) / num_network_groups + LOG.info("Number of group spread sandboxes: %s" % str(group_spread_sandboxes)) + + network_groups = [] + networks_per_group = num_networks / num_network_groups + base = 0 + for i in range(0, num_network_groups): + LOG.info("Group %d" % i) + network_group = [] + + for j in range(base, base + networks_per_group): + network_group.append(logical_networks[j]) + LOG.info("\t%d, add logical network: %s" % (j, logical_networks[j].get_lswitch())) + + network_groups.append(network_group) + base += networks_per_group + + LOG.info("Allocating sandboxes...") + sandbox_idx = 0 + base = 0 + for group in network_groups: + for network in group: + LOG.info("network switch name: %s" % network.get_lswitch()) + for sandbox_idx in range(base, base + group_spread_sandboxes): + network.add_sandbox(sandboxes[sandbox_idx]) + LOG.info("\tAdd sandbox %s" % sandboxes[sandbox_idx]) + base += group_spread_sandboxes + + return logical_networks class OvnNetwork(ovn.OvnScenario): """scenarios for OVN network.""" @@ -35,6 +127,7 @@ def create_networks(self, network_create_args): @scenario.configure(context={}) def create_and_bind_ports(self, network_create_args=None, + networks_per_sandbox=None, port_create_args=None, ports_per_network=None, port_bind_args=None): @@ -42,9 +135,15 @@ def create_and_bind_ports(self, sandboxes = self.context["sandboxes"] lswitches = self._create_networks(network_create_args) - for lswitch in lswitches: - lports = self._create_lports(lswitch, port_create_args, ports_per_network) - self._bind_ports(lports, sandboxes, port_bind_args) + logical_networks = [] + logical_networks = initialize_logical_networks(lswitches) + if networks_per_sandbox == None: + networks_per_sandbox = 0 + logical_networks = allocate_networks_on_sandboxes(logical_networks, sandboxes, networks_per_sandbox) + + for logical_network in logical_networks: + lports = self._create_lports(logical_network.get_lswitch(), port_create_args, ports_per_network) + self._bind_ports(lports, logical_network.get_sandboxes(), port_bind_args) def bind_ports(self):