In a sandbox run: $ ./ovn-lb-benchmark.sh <N> <VIPS> <BACKENDS> <USE_TEMPLATES>
to simulate an ovn-k8s-like topology with N nodes, VIPS NodePort services applied to all nodes. Each service has BACKENDS backends. If USE_TEMPLATES is "yes" then the configuration will be optimized to use Chassis_Template_Vars. Otherwise it will create N LBs per service, one for every node. Signed-off-by: Dumitru Ceara <[email protected]> --- V3: - Address Mark's comments: - use argparse instead of getopt. - make the benchmark script a bit more "pythonic". - Changed the benchmark part that was initially in bash and rewrote it in python. It should make a bit more sense like that. --- tutorial/automake.mk | 4 + tutorial/ovn-lb-benchmark.py | 234 ++++++++++++++++++++++++++++++++++++++++++ tutorial/ovn-lb-benchmark.sh | 31 ++++++ 3 files changed, 268 insertions(+), 1 deletion(-) create mode 100755 tutorial/ovn-lb-benchmark.py create mode 100755 tutorial/ovn-lb-benchmark.sh diff --git a/tutorial/automake.mk b/tutorial/automake.mk index 046962c000..29b1e006d2 100644 --- a/tutorial/automake.mk +++ b/tutorial/automake.mk @@ -1,6 +1,8 @@ EXTRA_DIST += \ tutorial/ovs-sandbox \ - tutorial/ovn-setup.sh + tutorial/ovn-setup.sh \ + tutorial/ovn-lb-benchmark.sh \ + tutorial/ovn-lb-benchmark.py sandbox: all cd $(srcdir)/tutorial && MAKE=$(MAKE) HAVE_OPENSSL=$(HAVE_OPENSSL) \ ./ovs-sandbox -b $(abs_builddir) --ovs-src $(ovs_srcdir) --ovs-build $(ovs_builddir) $(SANDBOXFLAGS) diff --git a/tutorial/ovn-lb-benchmark.py b/tutorial/ovn-lb-benchmark.py new file mode 100755 index 0000000000..658beb94ce --- /dev/null +++ b/tutorial/ovn-lb-benchmark.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python3 +import argparse +import sys + +import ovs.db.idl +import ovs.poller +import ovs.stream +import ovs.vlog +from ovs.db import error + +vlog = ovs.vlog.Vlog('template-lb-stress') +vlog.set_levels_from_string('console:info') +vlog.init(None) + +SCHEMA = '../ovn-nb.ovsschema' + + +def die(msg): + vlog.warn(f'Fatal error encountered: {msg}') + sys.exit(1) + + +def create_topology(idl, n): + vlog.info('Creating topology') + txn = ovs.db.idl.Transaction(idl) + lbg = txn.insert(idl.tables['Load_Balancer_Group']) + lbg.name = 'lbg' + + vlog.info('Adding join switch') + join_sw = txn.insert(idl.tables['Logical_Switch']) + join_sw.name = 'join' + + cluster_rtr = txn.insert(idl.tables['Logical_Router']) + cluster_rtr.name = 'cluster' + + rcj = txn.insert(idl.tables['Logical_Router_Port']) + rcj.name = 'rcj' + rcj.mac = '00:00:00:00:00:01' + rcj.networks = ['10.0.0.1/8'] + cluster_rtr.addvalue('ports', rcj.uuid) + + sjc = txn.insert(idl.tables['Logical_Switch_Port']) + sjc.name = 'sjc' + sjc.type = 'router' + sjc.addresses = ['router'] + sjc.setkey('options', 'router-port', 'rcj') + join_sw.addvalue('ports', sjc.uuid) + + for i in range(n): + vlog.info(f'Provisioning node {i}') + chassis = f'chassis-{i}' + gwr = txn.insert(idl.tables['Logical_Router']) + gwr.name = f'lr-{i}' + gwr.addvalue('load_balancer_group', lbg.uuid) + gwr.setkey('options', 'chassis', chassis) + + gwr2join = txn.insert(idl.tables['Logical_Router_Port']) + gwr2join.name = f'lr2j-{i}' + gwr2join.mac = '00:00:00:00:00:01' + gwr2join.networks = ['10.0.0.1/8'] + gwr.addvalue('ports', gwr2join.uuid) + + join2gwr = txn.insert(idl.tables['Logical_Switch_Port']) + join2gwr.name = f'j2lr-{i}' + join2gwr.type = 'router' + join2gwr.addresses = ['router'] + join2gwr.setkey('options', 'router-port', gwr2join.name) + join_sw.addvalue('ports', join2gwr.uuid) + + s = txn.insert(idl.tables['Logical_Switch']) + s.name = f'ls-{i}' + s.addvalue('load_balancer_group', lbg.uuid) + + cluster2s = txn.insert(idl.tables['Logical_Router_Port']) + cluster2s.name = f'c2s-{i}' + cluster2s.mac = '00:00:00:00:00:01' + cluster2s.networks = ['10.0.0.1/8'] + cluster_rtr.addvalue('ports', cluster2s.uuid) + + gw_chassis = txn.insert(idl.tables['Gateway_Chassis']) + gw_chassis.name = f'{cluster2s.name}-{chassis}' + gw_chassis.chassis_name = chassis + gw_chassis.priority = 1 + cluster2s.addvalue('gateway_chassis', gw_chassis.uuid) + + s2cluster = txn.insert(idl.tables['Logical_Switch_Port']) + s2cluster.name = f's2c-{i}' + s2cluster.type = 'router' + s2cluster.addresses = ['router'] + s2cluster.setkey('options', 'router-port', cluster2s.name) + s.addvalue('ports', s2cluster.uuid) + + lsp = txn.insert(idl.tables['Logical_Switch_Port']) + lsp.name = f'lsp-{i}' + s.addvalue('ports', lsp.uuid) + + if txn.commit_block() != ovs.db.idl.Transaction.SUCCESS: + die(f'Failed to create topology ({txn.get_error()}') + + +def add_template_lbs(idl, n, n_vips): + lbg = next(iter(idl.tables['Load_Balancer_Group'].rows.values())) + + for i in range(n_vips): + vlog.info(f'Adding LB {i}') + txn = ovs.db.idl.Transaction(idl) + lb = txn.insert(idl.tables['Load_Balancer']) + lb.name = f'lb-{i}' + lb.setkey('options', 'template', 'true') + lb.setkey('options', 'address-family', 'ipv4') + lb.setkey('vips', f'^vip:{i}', f'^backends{i}') + lb.protocol = 'tcp' + lbg.addvalue('load_balancer', lb.uuid) + if txn.commit_block() != ovs.db.idl.Transaction.SUCCESS: + die(f'Failed to add LB ({txn.get_error()}') + + +def add_chassis_template_vars(idl, n, n_vips, n_backends): + for i in range(n): + vlog.info(f'Adding LB vars for node {i}') + txn = ovs.db.idl.Transaction(idl) + tv = txn.insert(idl.tables['Chassis_Template_Var']) + tv.chassis = f'chassis-{i}' + tv.setkey('variables', 'vip', f'42.42.42.{i}') + + for j in range(n_vips): + port = j + 1 + j1 = (j + 1) // 250 + j2 = (j + 1) % 250 + backends = [f'42.{k}.{j1}.{j2}:{port}' for k in range(n_backends)] + tv.setkey('variables', f'backends{j}', ','.join(backends)) + if txn.commit_block() != ovs.db.idl.Transaction.SUCCESS: + die(f'Failed to add template vars ({txn.get_error()}') + + +def find_by_name(idl, table, name): + for row in idl.tables[table].rows.values(): + if row.name == name: + return row + return None + + +def add_explicit_lbs(idl, n, n_vips, n_backends): + for i in range(n): + lr = find_by_name(idl, 'Logical_Router', f'lr-{i}') + ls = find_by_name(idl, 'Logical_Switch', f'ls-{i}') + for j in range(n_vips): + vlog.info(f'Adding LB {j} for node {i}') + txn = ovs.db.idl.Transaction(idl) + port = j + 1 + j1 = (j + 1) // 250 + j2 = (j + 1) % 250 + backends = [f'42.{k}.{j1}.{j2}:{port}' for k in range(n_backends)] + + lb = txn.insert(idl.tables['Load_Balancer']) + lb.name = f'lb-{j}-{i}' + lb.setkey('vips', f'42.42.42.{i}:{port}', f'{",".join(backends)}') + lb.protocol = 'tcp' + lr.addvalue('load_balancer', lb.uuid) + ls.addvalue('load_balancer', lb.uuid) + if txn.commit_block() != ovs.db.idl.Transaction.SUCCESS: + die(f'Failed to add LB ({txn.get_error()}') + + +def run(remote, n, n_vips, n_backends, templates): + schema_helper = ovs.db.idl.SchemaHelper(SCHEMA) + schema_helper.register_all() + idl = ovs.db.idl.Idl(remote, schema_helper, leader_only=False) + + seqno = 0 + + error, stream = ovs.stream.Stream.open_block( + ovs.stream.Stream.open(remote), 2000 + ) + if error: + sys.stderr.write(f'failed to connect to \"{remote}\"') + sys.exit(1) + + if not stream: + sys.stderr.write(f'failed to connect to \"{remote}\"') + sys.exit(1) + rpc = ovs.jsonrpc.Connection(stream) + + while idl.change_seqno == seqno and not idl.run(): + rpc.run() + + poller = ovs.poller.Poller() + idl.wait(poller) + rpc.wait(poller) + poller.block() + + create_topology(idl, n) + if templates: + add_template_lbs(idl, n, n_vips) + add_chassis_template_vars(idl, n, n_vips, n_backends) + else: + add_explicit_lbs(idl, n, n_vips, n_backends) + + +def main(argv): + parser = argparse.ArgumentParser() + parser.add_argument( + '-n', '--nodes', type=int, required=True, help='Number of nodes' + ) + parser.add_argument( + '-v', '--vips', type=int, required=True, help='Number of LB VIPs' + ) + parser.add_argument( + '-b', + '--backends', + type=int, + required=True, + help='Number backends per VIP', + ) + parser.add_argument( + '-r', '--remote', required=True, help='NB connection string' + ) + parser.add_argument( + '-t', + '--template', + action='store_true', + help='Use LB Templates?', + ) + parser.set_defaults(template=False) + args = parser.parse_args() + run(args.remote, args.nodes, args.vips, args.backends, args.template) + + +if __name__ == '__main__': + try: + main(sys.argv) + except error.Error as e: + sys.stderr.write(f'{e}\n') + sys.exit(1) diff --git a/tutorial/ovn-lb-benchmark.sh b/tutorial/ovn-lb-benchmark.sh new file mode 100755 index 0000000000..5c30adf38a --- /dev/null +++ b/tutorial/ovn-lb-benchmark.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +nrtr=$1 +nlb=$2 +nbackends=$3 +use_template=$4 + +echo "ROUTERS : $nrtr" +echo "LBS : $nlb" +echo "BACKENDS PER LB: $nbackends" +echo "USE TEMPLATE : ${use_template}" + +if [ "${use_template}" = "yes" ]; then + templates=-t +else + templates= +fi + +python ovn-lb-benchmark.py -n $nrtr -v $nlb -b $nbackends \ + -r unix:$PWD/sandbox/nb1.ovsdb $templates + +# Bind a port from the first LS locally. +ovs-vsctl add-port br-int lsp-1 \ + -- set interface lsp-1 external_ids:iface-id=lsp-1 + +# Ensure everything was propagated to SB. +ovn-nbctl --wait=sb sync + +# Compact resulting DBs. +ovs-appctl -t $PWD/sandbox/nb1 ovsdb-server/compact +ovs-appctl -t $PWD/sandbox/sb1 ovsdb-server/compact _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
