On Tue, Nov 22, 2022 at 6:15 AM Dumitru Ceara <[email protected]> wrote:
>
> 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
>

Better to add a brief help message for the .sh itself. Otherwise, it
invokes the .py and outputs the help message of the .py, which is a little
misleading (although it is fairly easy to figure out by just looking into
the .sh script :) )

Acked-by: Han Zhou <[email protected]>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to