Add support for SRIOV NIC
Change-Id: I706b1bae8afb2e83c6a71e3396124f32a11fdb1f
This commit is contained in:
parent
b276f864ec
commit
a83508a4c4
30
compute.py
30
compute.py
@ -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()
|
||||||
|
@ -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
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
21
instance.py
21
instance.py
@ -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
|
||||||
|
20
network.py
20
network.py
@ -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
30
vmtp.py
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user