From 411e3dac2eb4c0d643e825bf5df455c93952fa6e Mon Sep 17 00:00:00 2001 From: AJAY KALAMBUR Date: Wed, 25 Feb 2015 12:43:33 -0800 Subject: [PATCH] IPV6 changes for VMTP Change-Id: I4958a119ff9dff661fb04f1973d76c4c3ee645f8 --- cfg.default.yaml | 14 ++++++++++ compute.py | 35 +++++++++++++++++++------ doc/source/usage.rst | 11 ++++++++ instance.py | 19 ++++++++++++-- network.py | 62 ++++++++++++++++++++++++++++++++++++++------ nuttcp_tool.py | 7 ++++- perf_tool.py | 5 +++- vmtp.py | 5 ++-- 8 files changed, 136 insertions(+), 22 deletions(-) diff --git a/cfg.default.yaml b/cfg.default.yaml index ca2e399..18f1eec 100644 --- a/cfg.default.yaml +++ b/cfg.default.yaml @@ -68,6 +68,14 @@ reuse_existing_vm : # An option of config_drive to True is provided to nova boot to enable this config_drive: +# ipv6 mode. Set this to one of the following 3 modes +# slaac : VM obtains IPV6 address from Openstack radvd using SLAAC +# dhcpv6-stateful : VM obtains ipv6 address from dnsmasq using DHCPv6 stateful +# dhcpv6-stateless : VM obtains ipv6 address from Openstack radvd using SLAAC and options from dnsmasq +# If left blank use ipv4 +ipv6_mode: + + # Default name for the router to use to connect the internal mgmt network # with the external network. If a router exists with this name it will be # reused, otherwise a new router will be created @@ -82,9 +90,15 @@ internal_network_name: ['pns-internal-net', 'pns-internal-net2'] # Name of the subnets associated to the internal mgmt network internal_subnet_name: ['pns-internal-subnet', 'pns-internal-subnet2'] +# Name of the subnets for ipv6 +internal_subnet_name_ipv6: ['pns-internal-v6-subnet','pns-internal-v6-subnet2'] + # Default CIDRs to use for the internal mgmt subnet internal_cidr: ['192.168.1.0/24' , '192.168.2.0/24'] +# Default CIDRs to use for data network for ipv6 +internal_cidr_v6: ['2001:45::/64','2001:46::/64'] + # The public key to use to ssh to all targets (VMs, containers, hosts) # If starting with './' is relative to the location of the VMTP script # else can be an absolute path diff --git a/compute.py b/compute.py index 4aacd54..d80800b 100644 --- a/compute.py +++ b/compute.py @@ -395,6 +395,12 @@ class Compute(object): ip_protocol="icmp", from_port=-1, to_port=-1) + if self.config.ipv6_mode: + self.novaclient.security_group_rules.create(group.id, + ip_protocol="icmp", + from_port=-1, + to_port=-1, + cidr="::/0") # Allow SSH traffic self.novaclient.security_group_rules.create(group.id, ip_protocol="tcp", @@ -404,11 +410,24 @@ class Compute(object): # 5001: Data traffic (standard iperf data port) # 5002: Control traffic (non standard) # note that 5000/tcp is already picked by openstack keystone - self.novaclient.security_group_rules.create(group.id, - ip_protocol="tcp", - from_port=5001, - to_port=5002) - self.novaclient.security_group_rules.create(group.id, - ip_protocol="udp", - from_port=5001, - to_port=5001) + if not self.config.ipv6_mode: + self.novaclient.security_group_rules.create(group.id, + ip_protocol="tcp", + from_port=5001, + to_port=5002) + self.novaclient.security_group_rules.create(group.id, + ip_protocol="udp", + from_port=5001, + to_port=5001) + else: + # IPV6 rules addition + self.novaclient.security_group_rules.create(group.id, + ip_protocol="tcp", + from_port=5001, + to_port=5002, + cidr="::/0") + self.novaclient.security_group_rules.create(group.id, + ip_protocol="udp", + from_port=5001, + to_port=5001, + cidr="::/0") diff --git a/doc/source/usage.rst b/doc/source/usage.rst index f05e637..8e44a35 100644 --- a/doc/source/usage.rst +++ b/doc/source/usage.rst @@ -241,3 +241,14 @@ The first IP passed (*--host*) is always the one running the server side. Option **Note:** Prior to running, the VMTP public key must be installed on each VM. +Example 7: IPV6 throughput measurement +"""""""""""""""""""""""""""""""""""""""" + +It is possible to use VMTP to measure throughput for ipv6 + +Set ipv6_mode to slaac, dhcpv6-stateful or dhcpv6-stateless. If SLAAC or DHCPv6 stateless is enabled make sure to have +radvd packaged in as part of openstack install. For DHCPv6 stateful you need dnsmasq version >= 2.68. The test creates +2 networks and creates 1 ipv4 and 1 ipv6 subnet inside each of these networks. The subnets are created based on the ipv6 +mode that you set in the configuration file. The floatingip result case is skipped for ipv6 since there is no concept of +a floating ip with ipv6. + diff --git a/instance.py b/instance.py index a789766..7533aac 100644 --- a/instance.py +++ b/instance.py @@ -18,9 +18,12 @@ import re import stat import subprocess + import monitor import sshutils + +from netaddr import IPAddress # a dictionary of sequence number indexed by a name prefix prefix_seq = {} @@ -108,7 +111,20 @@ class Instance(object): # Assume management network has direct access if self.config.reuse_network_name: self.ssh_ip = self.instance.networks[internal_network_name][0] + self.internal_ip = self.ssh_ip else: + # Set the internal ip to the correct ip for v4 and v6 + for ip_address in self.instance.networks[internal_network_name]: + ip = IPAddress(ip_address) + if self.config.ipv6_mode: + if ip.version == 6: + self.internal_ip = ip_address + else: + ipv4_fixed_address = ip_address + else: + if ip.version == 4: + self.internal_ip = ip_address + ipv4_fixed_address = ip_address fip = self.net.create_floating_ip() if not fip: self.display('Floating ip creation failed') @@ -117,9 +133,8 @@ class Instance(object): self.ssh_ip_id = fip['floatingip']['id'] self.buginf('Floating IP %s created', self.ssh_ip) self.buginf('Started - associating floating IP %s', self.ssh_ip) - self.instance.add_floating_ip(self.ssh_ip) + self.instance.add_floating_ip(self.ssh_ip, ipv4_fixed_address) # extract the IP for the data network - self.internal_ip = self.instance.networks[internal_network_name][0] self.buginf('Internal network IP: %s', self.internal_ip) self.buginf('SSH IP: %s', self.ssh_ip) diff --git a/network.py b/network.py index bf413f8..70580a2 100755 --- a/network.py +++ b/network.py @@ -40,6 +40,8 @@ class Network(object): # - second for network to network communication self.vm_int_net = [] self.ext_router_name = None + # Store state if the network is ipv4/ipv6 dual stack + self.ipv6_enabled = False # If reusing existing management network just find this network if self.config.reuse_network_name: @@ -94,13 +96,27 @@ class Network(object): print '[%s] Created ext router' % (self.ext_router_name) self.ext_router_created = True - # Create the 2 internal networks - for (net, subnet, cidr) in zip(config.internal_network_name, - config.internal_subnet_name, - config.internal_cidr): - int_net = self.create_net(net, subnet, cidr, - config.dns_nameservers) - self.vm_int_net.append(int_net) + if config.ipv6_mode: + self.ipv6_enabled = True + + # Create the networks and subnets depending on v4 or v6 + if config.ipv6_mode: + for (net, subnet, cidr, subnet_ipv6, cidr_ipv6) in zip(config.internal_network_name, + config.internal_subnet_name, + config.internal_cidr, + config.internal_subnet_name_ipv6, + config.internal_cidr_v6): + int_net = self.create_net(net, subnet, cidr, + config.dns_nameservers, + subnet_ipv6, cidr_ipv6, config.ipv6_mode) + self.vm_int_net.append(int_net) + else: + for (net, subnet, cidr) in zip(config.internal_network_name, + config.internal_subnet_name, + config.internal_cidr): + int_net = self.create_net(net, subnet, cidr, + config.dns_nameservers) + self.vm_int_net.append(int_net) # Add both internal networks to router interface to enable network to network connectivity self.__add_router_interface() @@ -109,7 +125,8 @@ class Network(object): # Check first if a network with the same name exists, if it exists # return that network. # dns_nameservers: a list of name servers e.g. ['8.8.8.8'] - def create_net(self, network_name, subnet_name, cidr, dns_nameservers): + def create_net(self, network_name, subnet_name, cidr, dns_nameservers, + subnet_name_ipv6=None, cidr_ipv6=None, ipv6_mode=None): for network in self.networks: if network['name'] == network_name: @@ -137,6 +154,22 @@ class Network(object): subnet = self.neutron_client.create_subnet(body)['subnet'] # add subnet id to the network dict since it has just been added network['subnets'] = [subnet['id']] + # If ipv6 is enabled than create and add ipv6 network + if ipv6_mode: + body = { + 'subnet': { + 'name': subnet_name_ipv6, + 'cidr': cidr_ipv6, + 'network_id': network['id'], + 'enable_dhcp': True, + 'ip_version': 6, + 'ipv6_ra_mode': ipv6_mode, + 'ipv6_address_mode': ipv6_mode + } + } + subnet = self.neutron_client.create_subnet(body)['subnet'] + # add the subnet id to the network dict + network['subnets'].append(subnet['id']) print 'Created internal network: %s' % (network_name) return network @@ -180,11 +213,24 @@ class Network(object): self.neutron_client.add_interface_router(self.ext_router['id'], body) if self.config.debug: print 'Ext router associated to ' + int_net['name'] + # If ipv6 is enabled than add second subnet + if self.ipv6_enabled: + body = { + 'subnet_id': int_net['subnets'][1] + } + self.neutron_client.add_interface_router(self.ext_router['id'], body) # Detach the ext router from the mgmt network def __remove_router_interface(self): for int_net in self.vm_int_net: if int_net: + # If ipv6 is enabled remove that subnet too + if self.ipv6_enabled: + body = { + 'subnet_id': int_net['subnets'][1] + } + self.neutron_client.remove_interface_router(self.ext_router['id'], + body) body = { 'subnet_id': int_net['subnets'][0] } diff --git a/nuttcp_tool.py b/nuttcp_tool.py index bf3295f..eab3029 100644 --- a/nuttcp_tool.py +++ b/nuttcp_tool.py @@ -25,7 +25,10 @@ class NuttcpTool(PerfTool): def get_server_launch_cmd(self): '''Return the commands to launch the server side.''' - return [self.dest_path + ' -P5002 -S --single-threaded &'] + if self.instance.config.ipv6_mode: + return [self.dest_path + ' -P5002 -S --single-threaded -6 &'] + else: + return [self.dest_path + ' -P5002 -S --single-threaded &'] def run_client(self, target_ip, target_instance, mss=None, bandwidth=0, bidirectional=False): @@ -117,6 +120,8 @@ class NuttcpTool(PerfTool): opts += " -F -r" if length: opts += " -l" + str(length) + if self.instance.config.ipv6_mode: + opts += " -6 " if udp: opts += " -u" # for UDP if the bandwidth is not provided we need to calculate diff --git a/perf_tool.py b/perf_tool.py index 2f8841b..72764c4 100644 --- a/perf_tool.py +++ b/perf_tool.py @@ -238,7 +238,10 @@ class PingTool(PerfTool): 5 packets transmitted, 5 received, 0% packet loss, time 3998ms rtt min/avg/max/mdev = 0.455/0.528/0.596/0.057 ms ''' - cmd = "ping -c " + str(ping_count) + " " + str(target_ip) + if self.instance.config.ipv6_mode: + cmd = "ping6 -c " + str(ping_count) + " " + str(target_ip) + else: + cmd = "ping -c " + str(ping_count) + " " + str(target_ip) cmd_out = self.instance.exec_command(cmd) if not cmd_out: res = {'protocol': 'ICMP', diff --git a/vmtp.py b/vmtp.py index 2a75360..86826a8 100755 --- a/vmtp.py +++ b/vmtp.py @@ -364,8 +364,9 @@ class VmtpTest(object): self.measure_flow("VM to VM different network fixed IP", self.server.internal_ip) - self.measure_flow("VM to VM different network floating IP", - self.server.ssh_ip) + if not config.ipv6_mode: + self.measure_flow("VM to VM different network floating IP", + self.server.ssh_ip) self.client.dispose() self.client = None