Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add load balancer support. #218

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rally_ovs/plugins/ovs/context/datapath.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ def setup(self):
"lswitches": lswitches,
}
self.context["ovs-internal-ports"] = {}
self.context["ovn-nb-lbs"] = {}

def cleanup(self):
pass # Not implemented
2 changes: 2 additions & 0 deletions rally_ovs/plugins/ovs/context/ovn_nb.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ def setup(self):

ovn_nbctl = self._get_ovn_controller(self.install_method)
lswitches = ovn_nbctl.show()
load_balancers = ovn_nbctl.lb_list()

self.context["ovn-nb"] = lswitches
self.context["ovn-nb-lbs"] = load_balancers
self.context["ovs-internal-ports"] = {}

@logging.log_task_wrapper(LOG.info, _("Exit context: `ovn_nb`"))
Expand Down
15 changes: 14 additions & 1 deletion rally_ovs/plugins/ovs/ovnclients.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,20 @@ def _create_lswitches(self, lswitch_create_args, num_switches=-1):
if start_cidr:
cidr = start_cidr.next(i)
name = "lswitch_%s" % cidr
lb_backend = '{}:4242'.format(netaddr.IPAddress(cidr) + 1)
self._add_lswitch_load_balancers(lb_backend)
lbs = list(self.context["ovn-nb-lbs"].keys())
else:
name = self.generate_random_name()
lbs = []

other_cfg = {
'mcast_snoop': mcast_snoop,
'mcast_idle_timeout': mcast_idle,
'mcast_table_size': mcast_table_size
}

lswitch = ovn_nbctl.lswitch_add(name, other_cfg)
lswitch = ovn_nbctl.lswitch_add(name, other_cfg, lbs)
if start_cidr:
lswitch["cidr"] = cidr

Expand All @@ -101,6 +105,15 @@ def _create_lswitches(self, lswitch_create_args, num_switches=-1):
ovn_nbctl.enable_batch_mode(False)
return lswitches

def _add_lswitch_load_balancers(self, backend):
lbs = []
for lb_name, (lb_vip, lb_backends) in self.context["ovn-nb-lbs"].items():
new_backends = self._add_load_balancer_backend(lb_name, lb_vip, lb_backends, backend)
lbs.append((lb_name, lb_vip, new_backends))

for lb_name, lb_vip, new_backends in lbs:
self.context["ovn-nb-lbs"][lb_name] = (lb_vip, new_backends)

def _create_routers(self, router_create_args):
self.RESOURCE_NAME_FORMAT = "lrouter_XXXXXX_XXXXXX"

Expand Down
23 changes: 23 additions & 0 deletions rally_ovs/plugins/ovs/ovsclients.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,29 @@ def parse_lswitch_list(lswitch_data):
lswitches.append({"name": line.split(" ")[1].strip('()')})
return lswitches

def get_lb_info(info):
lbs = {}

lb_name = None
for line in info.splitlines():
line = line.strip()
if len(line) == 0:
continue

if not lb_name:
lb_name = line
else:
tokens = line.split('=')
if len(tokens) == 0:
lb_name = None
continue
lb_vip = tokens[0]
lb_backends = tokens[1] if len(tokens) == 2 else ''
lbs[lb_name] = (lb_vip, lb_backends)
lb_name = None

Comment on lines +187 to +195
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the = token is not found, then split() will return a list containing the whole string as the first item. Therefore, len(tokens) wont' ever be 0.

You can just replace this whole block with

lbs[lb_name] = tuple(line.split('=', 2))
lb_name = None

This will ensure that lbs[lb_name] is set to a tuple of 2 items. If the = is not present, then the second item of the tuple will be an empty string.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Way better, will do, thanks!

return lbs

def set_colval_args(*col_values):
args = []
for entry in col_values:
Expand Down
28 changes: 24 additions & 4 deletions rally_ovs/plugins/ovs/ovsclients_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,11 @@ def lrouter_nat_add(self, lrouter, nat_type, external_ip, logical_ip):
params = [lrouter, nat_type, external_ip, logical_ip]
self.run("lr-nat-add", args=params)

def lswitch_add(self, name, other_cfg={}):
params = [name]

def lswitch_add(self, name, other_cfg={}, lbs=[]):
self.run("ls-add", args=[name])

self.run("ls-add", args=params)
for lb in lbs:
self.run("ls-lb-add", args=[name, lb])

for cfg, val in other_cfg.items():
param_cfg = 'other_config:{c}="{v}"'.format(c=cfg, v=val)
Expand Down Expand Up @@ -282,6 +282,26 @@ def port_group_del(self, port_group):
params = [port_group]
self.run("pg-del", [], params)

def lb_add(self, lb_name, lb_vip, lb_proto):
params = [lb_name, lb_vip, '\'\'', lb_proto]
self.run("lb-add", [], params)

def lb_set_vip_backends(self, lb_name, lb_vip, lb_backends):
vips_set='vips=\'\"{}\"=\"{}\"\''.format(lb_vip, lb_backends)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, you can get rid of the \ before the double quotes. It might make the line a bit easier to read.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack, thanks.

params = ['Load_Balancer', lb_name, vips_set]
self.run('set', args=params)

def lb_list(self, lb_name=None):
opts = ['--bare', '--columns', 'name,vips']
params = ['Load_Balancer']
if lb_name:
params.append(lb_name)
stdout = StringIO()
self.run('list', opts=opts, args=params, stdout=stdout)
output = stdout.getvalue()

return get_lb_info(output)

def show(self, lswitch=None):
params = [lswitch] if lswitch else []
stdout = StringIO()
Expand Down
19 changes: 19 additions & 0 deletions rally_ovs/plugins/ovs/scenarios/ovn.py
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,25 @@ def _get_address_set(self, set_name):
ovn_nbctl = self._get_ovn_controller(self.install_method)
return ovn_nbctl.get("Address_Set", set_name, 'addresses')

@atomic.action_timer("ovn.create_load_balancer")
def _create_load_balancer(self, lb_name, lb_vip, lb_proto):
LOG.info('Create load balancer method: %s' % self.install_method)
ovn_nbctl = self._get_ovn_controller(self.install_method)
ovn_nbctl.lb_add(lb_name, lb_vip, lb_proto)
self.context["ovn-nb-lbs"][lb_name] = lb_vip

@atomic.action_timer("ovn.add_load_balancer_backend")
def _add_load_balancer_backend(self, lb_name, lb_vip, lb_backends, lb_backend):
LOG.info('Add load balancer backend method: %s' % self.install_method)

if lb_backends != '':
backends = '{},{}'.format(lb_backends, lb_backend)
else:
backends = '{}'.format(lb_backend)
ovn_nbctl = self._get_ovn_controller(self.install_method)
ovn_nbctl.lb_set_vip_backends(lb_name, lb_vip, backends)
return backends

def runCmd(self, ssh, cmd="", pid_opt="", pid="", background_opt=False):
ssh.enable_batch_mode(False)

Expand Down
5 changes: 5 additions & 0 deletions rally_ovs/plugins/ovs/scenarios/ovn_fake_multinode.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,11 @@ def setup_switch_per_node(self, fake_multinode_args = {},
lport_create_args = {},
port_bind_args = {},
create_mgmt_port = True):
# TODO: figure out how to not reload the context?
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@putnopvut This is the hack I was talking about.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you go into a bit more detail about what's going on here? I've been away from this for long enough that I don't even know what it means to reload the context.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.context gets populated once before any iteration of the scenario is run, in class OvnNorthboundContext.setup(). Then, if I'm not wrong, each scenario gets a copy of the context. However, because we're updating load balancers in each iteration, ideally we would have to update the original context, such that upcoming iterations have the updated set of load balancers.

The rally runner doesn't allow that because iterations might be run in parallel, I presume.

To work around it I explicitly updated self.context["ovn-nb-lbs"] with the results of ovn_nbctl.lb_list().

ovn_nbctl = self._get_ovn_controller(self.install_method)
lswitches = ovn_nbctl.show()
self.context["ovn-nb-lbs"] = ovn_nbctl.lb_list()

self.connect_chassis_nodes(fake_multinode_args)
self.wait_chassis_nodes(fake_multinode_args)

Expand Down
12 changes: 9 additions & 3 deletions rally_ovs/plugins/ovs/scenarios/ovn_nb.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,17 @@ def configure_routed_lport(self, lswitch, lport_create_args, port_bind_args,
network_cidr = lswitch.get("cidr", None)
if network_cidr:
ip_list = netaddr.IPNetwork(network_cidr.ip + ip_start_index).iter_hosts()
ipaddr = str(next(ip_list))
ip = next(ip_list)
else:
ipaddr = ""
ip = None

ipaddr = ""
for i in range(batch):
lport = lports[i]
index = batch * self.context["iteration"] + i
if ip:
ipaddr = str(ip)
ip += 1

# create/update network policy
network_policy_index = index / network_policy_size
Expand Down Expand Up @@ -410,4 +414,6 @@ def create_and_remove_address_set(self, name, address_list):
self._create_address_set(name, address_list)
self._remove_address_set(name)


@scenario.configure(context={})
def create_load_balancer(self, lb_name, lb_vip, lb_proto):
self._create_load_balancer(lb_name, lb_vip, lb_proto)