Add support for SRIOV NIC

Change-Id: I706b1bae8afb2e83c6a71e3396124f32a11fdb1f
This commit is contained in:
ahothan 2015-03-11 16:56:44 -07:00
parent b276f864ec
commit a83508a4c4
5 changed files with 101 additions and 26 deletions

View File

@ -142,12 +142,16 @@ class Compute(object):
return net return net
# Create a server instance with name vmname # Create a server instance with name vmname
# if exists delete and recreate # and check that it gets into the ACTIVE state
def create_server(self, vmname, image, flavor, key_name, def create_server(self, vmname, image, flavor, key_name,
nic, sec_group, avail_zone=None, user_data=None, nic, sec_group, avail_zone=None, user_data=None,
config_drive=None, config_drive=None,
retry_count=10): retry_count=10):
if sec_group:
security_groups = [sec_group.id]
else:
security_groups = None
# Also attach the created security group for the test # Also attach the created security group for the test
instance = self.novaclient.servers.create(name=vmname, instance = self.novaclient.servers.create(name=vmname,
image=image, image=image,
@ -157,12 +161,26 @@ class Compute(object):
availability_zone=avail_zone, availability_zone=avail_zone,
userdata=user_data, userdata=user_data,
config_drive=config_drive, config_drive=config_drive,
security_groups=[sec_group.id]) security_groups=security_groups)
flag_exist = self.find_server(vmname, retry_count) if not instance:
if flag_exist:
return instance
else:
return None return None
# Verify that the instance gets into the ACTIVE state
for retry_attempt in range(retry_count):
instance = self.novaclient.servers.get(instance.id)
if instance.status == 'ACTIVE':
return instance
if instance.status == 'ERROR':
print 'Instance creation error:' + instance.fault['message']
break
if self.config.debug:
print "[%s] VM status=%s, retrying %s of %s..." \
% (vmname, instance.status, (retry_attempt + 1), retry_count)
time.sleep(2)
# instance not in ACTIVE state
print('Instance failed status=' + instance.status)
self.delete_server(instance)
return None
def get_server_list(self): def get_server_list(self):
servers_list = self.novaclient.servers.list() servers_list = self.novaclient.servers.list()

View File

@ -13,15 +13,16 @@ VMTP Usage
[--external-host <user>@<host_ssh_ip>[:password>]] [--external-host <user>@<host_ssh_ip>[:password>]]
[--controller-node <user>@<host_ssh_ip>[:<password>]] [--controller-node <user>@<host_ssh_ip>[:<password>]]
[--mongod_server <server ip>] [--json <file>] [--mongod_server <server ip>] [--json <file>]
[--tp-tool nuttcp|iperf] [--hypervisor [<az>:] <hostname>] [--tp-tool <nuttcp|iperf>] [--hypervisor [<az>:] <hostname>]
[--inter-node-only] [--protocols T|U|I] [--inter-node-only] [--protocols <T|U|I>]
[--bandwidth <bandwidth>] [--tcpbuf <tcp_pkt_size1,...>] [--bandwidth <bandwidth>] [--tcpbuf <tcp_pkt_size1,...>]
[--udpbuf <udp_pkt_size1,...>] [--no-env] [-d] [-v] [--udpbuf <udp_pkt_size1,...>] [--no-env]
[--vnic-type <direct|macvtap|normal>] [-d] [-v]
[--stop-on-error] [--vm_image_url <url_to_image>] [--stop-on-error] [--vm_image_url <url_to_image>]
[--test_description <test_description>] [--test_description <test_description>]
OpenStack VM Throughput V2.0.2 OpenStack VM Throughput V2.0.3
optional arguments: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
-c <config_file>, --config <config_file> -c <config_file>, --config <config_file>
@ -45,12 +46,12 @@ VMTP Usage
--mongod_server <server ip> --mongod_server <server ip>
provide mongoDB server IP to store results provide mongoDB server IP to store results
--json <file> store results in json format file --json <file> store results in json format file
--tp-tool nuttcp|iperf --tp-tool <nuttcp|iperf>
transport perf tool to use (default=nuttcp) transport perf tool to use (default=nuttcp)
--hypervisor [<az>:] <hostname> --hypervisor [<az>:] <hostname>
hypervisor to use (1 per arg, up to 2 args) hypervisor to use (1 per arg, up to 2 args)
--inter-node-only only measure inter-node --inter-node-only only measure inter-node
--protocols T|U|I protocols T(TCP), U(UDP), I(ICMP) - default=TUI (all) --protocols <T|U|I> protocols T(TCP), U(UDP), I(ICMP) - default=TUI (all)
--bandwidth <bandwidth> --bandwidth <bandwidth>
the bandwidth limit for TCP/UDP flows in K/M/Gbps, the bandwidth limit for TCP/UDP flows in K/M/Gbps,
e.g. 128K/32M/5G. (default=no limit) e.g. 128K/32M/5G. (default=no limit)
@ -61,6 +62,8 @@ VMTP Usage
list of buffer length when transmitting over UDP in list of buffer length when transmitting over UDP in
Bytes, e.g. --udpbuf 128,2048. (default=128,1024,8192) Bytes, e.g. --udpbuf 128,2048. (default=128,1024,8192)
--no-env do not read env variables --no-env do not read env variables
--vnic-type <direct|macvtap|normal>
binding vnic type for test VMs
-d, --debug debug flag (very verbose) -d, --debug debug flag (very verbose)
-v, --version print version of this script and exit -v, --version print version of this script and exit
--stop-on-error Stop and keep everything as-is on error (must cleanup --stop-on-error Stop and keep everything as-is on error (must cleanup
@ -71,7 +74,6 @@ VMTP Usage
--test_description <test_description> --test_description <test_description>
The test description to be stored in JSON or MongoDB The test description to be stored in JSON or MongoDB
Configuration File Configuration File
^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
@ -138,6 +140,12 @@ There is a candidate image defined in the default config already. It has been ve
**Note:** Due to the limitation of the Python glanceclient API (v2.0), it is not able to create the image directly from a remote URL. So the implementation of this feature used a glance CLI command instead. Be sure to source the OpenStack rc file first before running VMTP with this feature. **Note:** Due to the limitation of the Python glanceclient API (v2.0), it is not able to create the image directly from a remote URL. So the implementation of this feature used a glance CLI command instead. Be sure to source the OpenStack rc file first before running VMTP with this feature.
VNIC Type
^^^^^^^^^
By default test VMs will be created with ports that have a "normal" VNIC type.
To create test VMs with ports that use PCI passthrough SRIOV, specify "--vnic_type direct". This will assume that the host where the VM are instantiated have SRIOV capable NIC.
An exception will be thrown if a test VM is lauched on a host that does not have SRIOV capable NIC or has not been configured to use such feature.
Quick guide to run VMTP on an OpenStack Cloud Quick guide to run VMTP on an OpenStack Cloud
---------------------------------------------- ----------------------------------------------

View File

@ -49,6 +49,7 @@ class Instance(object):
self.ssh_user = config.ssh_vm_username self.ssh_user = config.ssh_vm_username
self.instance = None self.instance = None
self.ssh = None self.ssh = None
self.port = None
if config.gmond_svr_ip: if config.gmond_svr_ip:
self.gmond_svr = config.gmond_svr_ip self.gmond_svr = config.gmond_svr_ip
else: else:
@ -75,7 +76,7 @@ class Instance(object):
# and extract internal network IP # and extract internal network IP
# Retruns True if success, False otherwise # Retruns True if success, False otherwise
def create(self, image, flavor_type, def create(self, image, flavor_type,
keypair, nics, keypair, int_net,
az, az,
internal_network_name, internal_network_name,
sec_group, sec_group,
@ -90,6 +91,20 @@ class Instance(object):
user_data = open(init_file_name) user_data = open(init_file_name)
else: else:
user_data = None user_data = None
if self.config.vnic_type:
# create the VM by passing a port ID instead of a net ID
self.port = self.net.create_port(int_net['id'],
[sec_group.id],
self.config.vnic_type)
nics = [{'port-id': self.port['id']}]
# no need to create server with a security group since
# we already have the port created with it
sec_group = None
else:
# create the VM by passing a net ID
nics = [{'net-id': int_net['id']}]
self.instance = self.comp.create_server(self.name, self.instance = self.comp.create_server(self.name,
image, image,
flavor_type, flavor_type,
@ -104,7 +119,6 @@ class Instance(object):
user_data.close() user_data.close()
if not self.instance: if not self.instance:
self.display('Server creation failed') self.display('Server creation failed')
self.dispose()
return False return False
# If reusing existing management network skip the floating ip creation and association to VM # If reusing existing management network skip the floating ip creation and association to VM
@ -134,6 +148,7 @@ class Instance(object):
self.buginf('Floating IP %s created', self.ssh_ip) self.buginf('Floating IP %s created', self.ssh_ip)
self.buginf('Started - associating floating IP %s', self.ssh_ip) self.buginf('Started - associating floating IP %s', self.ssh_ip)
self.instance.add_floating_ip(self.ssh_ip, ipv4_fixed_address) self.instance.add_floating_ip(self.ssh_ip, ipv4_fixed_address)
# extract the IP for the data network # extract the IP for the data network
self.buginf('Internal network IP: %s', self.internal_ip) self.buginf('Internal network IP: %s', self.internal_ip)
self.buginf('SSH IP: %s', self.ssh_ip) self.buginf('SSH IP: %s', self.ssh_ip)
@ -295,6 +310,8 @@ class Instance(object):
self.comp.delete_server(self.instance) self.comp.delete_server(self.instance)
self.buginf('Instance deleted') self.buginf('Instance deleted')
self.instance = None self.instance = None
if self.port:
self.net.delete_port(self.port)
if self.ssh: if self.ssh:
self.ssh.close() self.ssh.close()
self.ssh = None self.ssh = None

View File

@ -284,6 +284,26 @@ class Network(object):
router = self.neutron_client.update_router(router_id, body) router = self.neutron_client.update_router(router_id, body)
return router['router'] return router['router']
# Create a port
def create_port(self, net_id, sec_group_list, vnic_type):
body = {
"port": {
"network_id": net_id,
"security_groups": sec_group_list
}
}
if vnic_type:
body['port']['binding:vnic_type'] = vnic_type
port = self.neutron_client.create_port(body)
if self.config.debug:
print 'Created port ' + port['port']['id']
return port['port']
def delete_port(self, port):
if self.config.debug:
print 'Deleting port ' + port['id']
self.neutron_client.delete_port(port['id'])
# Create a floating ip on the external network and return it # Create a floating ip on the external network and return it
def create_floating_ip(self): def create_floating_ip(self):
body = { body = {

30
vmtp.py
View File

@ -40,7 +40,7 @@ from neutronclient.v2_0 import client as neutronclient
from novaclient.client import Client from novaclient.client import Client
from novaclient.exceptions import ClientException from novaclient.exceptions import ClientException
__version__ = '2.0.2' __version__ = '2.0.3'
from perf_instance import PerfInstance as PerfInstance from perf_instance import PerfInstance as PerfInstance
@ -201,11 +201,10 @@ class VmtpTest(object):
# Create an instance on a particular availability zone # Create an instance on a particular availability zone
def create_instance(self, inst, az, int_net): def create_instance(self, inst, az, int_net):
nics = [{'net-id': int_net['id']}]
self.assert_true(inst.create(self.image_instance, self.assert_true(inst.create(self.image_instance,
self.flavor_type, self.flavor_type,
config.public_key_name, config.public_key_name,
nics, int_net,
az, az,
int_net['name'], int_net['name'],
self.sec_group)) self.sec_group))
@ -223,7 +222,6 @@ class VmtpTest(object):
nova_client = Client(**creds_nova) nova_client = Client(**creds_nova)
neutron = neutronclient.Client(**creds) neutron = neutronclient.Client(**creds)
self.comp = compute.Compute(nova_client, config) self.comp = compute.Compute(nova_client, config)
# Add the script public key to openstack # Add the script public key to openstack
self.comp.add_public_key(config.public_key_name, self.comp.add_public_key(config.public_key_name,
@ -419,7 +417,8 @@ class VmtpTest(object):
self.comp.remove_public_key(config.public_key_name) self.comp.remove_public_key(config.public_key_name)
# Finally remove the security group # Finally remove the security group
try: try:
self.comp.security_group_delete(self.sec_group) if self.comp:
self.comp.security_group_delete(self.sec_group)
except ClientException: except ClientException:
# May throw novaclient.exceptions.BadRequest if in use # May throw novaclient.exceptions.BadRequest if in use
print('Security group in use: not deleted') print('Security group in use: not deleted')
@ -432,8 +431,9 @@ class VmtpTest(object):
self.measure_vm_flows() self.measure_vm_flows()
except KeyboardInterrupt: except KeyboardInterrupt:
traceback.format_exc() traceback.format_exc()
except (VmtpException, sshutils.SSHError, ClientException): except (VmtpException, sshutils.SSHError, ClientException, Exception):
traceback.format_exc() print 'print_exc:'
traceback.print_exc()
error_flag = True error_flag = True
if opts.stop_on_error and error_flag: if opts.stop_on_error and error_flag:
@ -572,7 +572,7 @@ if __name__ == '__main__':
action='store', action='store',
default='nuttcp', default='nuttcp',
help='transport perf tool to use (default=nuttcp)', help='transport perf tool to use (default=nuttcp)',
metavar='nuttcp|iperf') metavar='<nuttcp|iperf>')
# note there is a bug in argparse that causes an AssertionError # note there is a bug in argparse that causes an AssertionError
# when the metavar is set to '[<az>:]<hostname>', hence had to insert a space # when the metavar is set to '[<az>:]<hostname>', hence had to insert a space
@ -590,7 +590,7 @@ if __name__ == '__main__':
action='store', action='store',
default='TUI', default='TUI',
help='protocols T(TCP), U(UDP), I(ICMP) - default=TUI (all)', help='protocols T(TCP), U(UDP), I(ICMP) - default=TUI (all)',
metavar='T|U|I') metavar='<T|U|I>')
parser.add_argument('--bandwidth', dest='vm_bandwidth', parser.add_argument('--bandwidth', dest='vm_bandwidth',
action='store', action='store',
@ -618,6 +618,12 @@ if __name__ == '__main__':
action='store_true', action='store_true',
help='do not read env variables') help='do not read env variables')
parser.add_argument('--vnic-type', dest='vnic_type',
default=None,
action='store',
help='binding vnic type for test VMs',
metavar='<direct|macvtap|normal>')
parser.add_argument('-d', '--debug', dest='debug', parser.add_argument('-d', '--debug', dest='debug',
default=False, default=False,
action='store_true', action='store_true',
@ -662,6 +668,12 @@ if __name__ == '__main__':
config.debug = opts.debug config.debug = opts.debug
config.inter_node_only = opts.inter_node_only config.inter_node_only = opts.inter_node_only
# direct: use SR-IOV ports for all the test VMs
if opts.vnic_type not in [None, 'direct', 'macvtap', 'normal']:
print('Invalid vnic-type: ' + opts.vnic_type)
sys.exit(1)
config.vnic_type = opts.vnic_type
config.hypervisors = opts.hypervisors config.hypervisors = opts.hypervisors
# time to run each perf test in seconds # time to run each perf test in seconds