Remove VMTP key pair and use user's key pair instead
make --controler-node callable refactor SSH management code standardize on ssh access argument format Change-Id: Ie0b422f2381a735621bb732686a167dc1a4ca3b5
This commit is contained in:
parent
0db3502c03
commit
f8c3d9d711
@ -99,17 +99,17 @@ 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
|
||||
public_key_file: './ssh/id_rsa.pub'
|
||||
# The public and private keys to use to ssh to all targets (VMs, containers, hosts)
|
||||
# By default the SSH library will try several methods to authenticate:
|
||||
# - password if provided on the command line
|
||||
# - user's own key pair (under the home directory $HOME) if already setup
|
||||
# - the below key pair if not empty
|
||||
# If you want to use a specific key pair, specify the key pair files here.
|
||||
# This can be a pathname that is absolute or relative to the current directory
|
||||
public_key_file:
|
||||
private_key_file:
|
||||
|
||||
# File containing the private key to use along with the publick key
|
||||
# If starting with './' is relative to the location of the script
|
||||
# else can be an absolute path
|
||||
private_key_file: './ssh/id_rsa'
|
||||
|
||||
# Name of the P&S public key in OpenStack
|
||||
# Name of the P&S public key in OpenStack to create for all test VMs
|
||||
public_key_name: 'pns_public_key'
|
||||
|
||||
# name of the server VM
|
||||
@ -122,6 +122,7 @@ vm_name_client: 'TestClient'
|
||||
security_group_name: 'pns-security'
|
||||
|
||||
# Location to the performance test tools.
|
||||
# If relative, is relative to the vmtp directory
|
||||
perf_tool_path: './tools'
|
||||
|
||||
# ping variables
|
||||
|
24
compute.py
24
compute.py
@ -114,11 +114,12 @@ class Compute(object):
|
||||
def create_keypair(self, name, private_key_pair_file):
|
||||
self.remove_public_key(name)
|
||||
keypair = self.novaclient.keypairs.create(name)
|
||||
# Now write the keypair to the file
|
||||
kpf = os.open(private_key_pair_file,
|
||||
os.O_WRONLY | os.O_CREAT, 0o600)
|
||||
with os.fdopen(kpf, 'w') as kpf:
|
||||
kpf.write(keypair.private_key)
|
||||
# Now write the keypair to the file if requested
|
||||
if private_key_pair_file:
|
||||
kpf = os.open(private_key_pair_file,
|
||||
os.O_WRONLY | os.O_CREAT, 0o600)
|
||||
with os.fdopen(kpf, 'w') as kpf:
|
||||
kpf.write(keypair.private_key)
|
||||
return keypair
|
||||
|
||||
# Add an existing public key to openstack
|
||||
@ -133,10 +134,21 @@ class Compute(object):
|
||||
print 'ERROR: Cannot open public key file %s: %s' % \
|
||||
(public_key_file, exc)
|
||||
return None
|
||||
print 'Adding public key %s' % (name)
|
||||
keypair = self.novaclient.keypairs.create(name, public_key)
|
||||
return keypair
|
||||
|
||||
def init_key_pair(self, kp_name, ssh_access):
|
||||
'''Initialize the key pair for all test VMs
|
||||
if a key pair is specified in access, use that key pair else
|
||||
create a temporary key pair
|
||||
'''
|
||||
if ssh_access.public_key_file:
|
||||
return self.add_public_key(kp_name, ssh_access.public_key_file)
|
||||
else:
|
||||
keypair = self.create_keypair(kp_name, None)
|
||||
ssh_access.private_key = keypair.private_key
|
||||
return keypair
|
||||
|
||||
def find_network(self, label):
|
||||
net = self.novaclient.networks.find(label=label)
|
||||
return net
|
||||
|
@ -88,7 +88,7 @@ class Credentials(object):
|
||||
# just check that they are present
|
||||
for varname in ['OS_USERNAME', 'OS_AUTH_URL', 'OS_TENANT_NAME']:
|
||||
if varname not in os.environ:
|
||||
print 'Warning: %s is missing' % (varname)
|
||||
# print 'Warning: %s is missing' % (varname)
|
||||
success = False
|
||||
if success:
|
||||
self.rc_username = os.environ['OS_USERNAME']
|
||||
|
@ -2,18 +2,32 @@
|
||||
Setup
|
||||
=====
|
||||
|
||||
Public Cloud
|
||||
------------
|
||||
|
||||
Public clouds are special because they may not expose all OpenStack APIs and may not allow all types of operations. Some public clouds have limitations in the way virtual networks can be used or require the use of a specific external router. Running VMTP against a public cloud will require a specific configuration file that takes into account those specificities.
|
||||
SSH Authentication
|
||||
------------------
|
||||
|
||||
Refer to the provided public cloud sample configuration files for more information.
|
||||
VMTP can optionally SSH to the following hosts:
|
||||
- OpenStack controller node (if the --controller-node option is used)
|
||||
- External host for cloud upload/download performance test (if the --external-host option is used)
|
||||
- Native host throughput (if the --host option is used)
|
||||
|
||||
To connect to these hosts, the SSH library used by VMTP will try a number of authentication methods:
|
||||
- if provided at the command line, try the provided password (e.g. --controller-node localadmin@10.1.1.78:secret)
|
||||
- user's personal private key (~/.ssh/id_rsa)
|
||||
- if provided in the configuration file, a specific private key file (private_key_file variable)
|
||||
|
||||
SSH to the test VMs is always based on key pairs with the following precedence:
|
||||
- if provided in the passed configuration file, use the configured key pair (private_key_file and public_key_file variables),
|
||||
- otherwise use the user's personal key pair (~/.ssh/id_rsa and ~/.ssh/id_rsa.pub)
|
||||
- otherwise if there is no personal key pair configured, create a temporary key pair to access all test VMs
|
||||
|
||||
To summarize:
|
||||
- if you have a personal key pair configured in your home directory, VMTP will use that key pair for all SSH connections (including to the test VMs)
|
||||
- if you want to use your personal key pair, there is nothing to do other than making sure that the targeted hosts have been configured with the associated public key
|
||||
|
||||
In any case make sure you specify the correct username.
|
||||
If there is a problem, you should see an error message and stack trace after the SSH library times out.
|
||||
|
||||
SSH Password-less Access
|
||||
------------------------
|
||||
|
||||
For host throughput (*--host*), VMTP expects the target hosts to be pre-provisioned with a public key in order to allow password-less SSH.
|
||||
|
||||
Test VMs are created through OpenStack by VMTP with the appropriate public key to allow password-less ssh. By default, VMTP uses a default VMTP public key located in ssh/id_rsa.pub, simply append the content of that file into the .ssh/authorized_keys file under the host login home directory).
|
||||
|
||||
**Note:** This default VMTP public key should only be used for transient test VMs and **MUST NOT** be used to provision native hosts since the corresponding private key is open to anybody! To use alternate key pairs, the 'private_key_file' variable in the configuration file must be overridden to point to the file containing the private key to use to connect with SSH.
|
||||
|
@ -9,19 +9,19 @@ VMTP Usage
|
||||
|
||||
usage: vmtp.py [-h] [-c <config_file>] [-r <openrc_file>]
|
||||
[-m <gmond_ip>[:<port>]] [-p <password>] [-t <time>]
|
||||
[--host <user>@<host_ssh_ip>[:<server-listen-if-name>]]
|
||||
[--host <user>@<host_ssh_ip>[:<password>:<server-listen-if-name>]]
|
||||
[--external-host <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>]
|
||||
[--inter-node-only] [--protocols <T|U|I>]
|
||||
[--bandwidth <bandwidth>] [--tcpbuf <tcp_pkt_size1,...>]
|
||||
[--udpbuf <udp_pkt_size1,...>] [--no-env]
|
||||
[--vnic-type <direct|macvtap|normal>] [-d] [-v]
|
||||
[--stop-on-error] [--vm_image_url <url_to_image>]
|
||||
[--test_description <test_description>]
|
||||
[--stop-on-error] [--vm-image-url <url_to_image>]
|
||||
[--test-description <test_description>]
|
||||
|
||||
OpenStack VM Throughput V2.0.4
|
||||
OpenStack VM Throughput V2.1.0
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
@ -35,15 +35,15 @@ VMTP Usage
|
||||
OpenStack password
|
||||
-t <time>, --time <time>
|
||||
throughput test duration in seconds (default 10 sec)
|
||||
--host <user>@<host_ssh_ip>[:<server-listen-if-name>]
|
||||
native host throughput (targets requires ssh key)
|
||||
--host <user>@<host_ssh_ip>[:<password>:<server-listen-if-name>]
|
||||
native host throughput (password or public key
|
||||
required)
|
||||
--external-host <user>@<host_ssh_ip>[:password>]
|
||||
external-VM throughput (host requires public key if no
|
||||
password)
|
||||
external-VM throughput (password or public key
|
||||
required)
|
||||
--controller-node <user>@<host_ssh_ip>[:<password>]
|
||||
controller node ssh (host requires public key if no
|
||||
password)
|
||||
--mongod_server <server ip>
|
||||
controller node ssh (password or public key required)
|
||||
--mongod-server <server ip>
|
||||
provide mongoDB server IP to store results
|
||||
--json <file> store results in json format file
|
||||
--tp-tool <nuttcp|iperf>
|
||||
@ -68,10 +68,10 @@ VMTP Usage
|
||||
-v, --version print version of this script and exit
|
||||
--stop-on-error Stop and keep everything as-is on error (must cleanup
|
||||
manually)
|
||||
--vm_image_url <url_to_image>
|
||||
--vm-image-url <url_to_image>
|
||||
URL to a Linux image in qcow2 format that can be
|
||||
downloaded from
|
||||
--test_description <test_description>
|
||||
--test-description <test_description>
|
||||
The test description to be stored in JSON or MongoDB
|
||||
|
||||
Configuration File
|
||||
@ -107,8 +107,12 @@ This file should then be passed to VMTP using the *-r* option or should be sourc
|
||||
Access Info for Controller Node
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
By default, VMTP is not able to get the Linux distro nor the OpenStack version of the cloud deployment under test. However, by providing the credentials of the controller node under test, VMTP will try to fetch these information, and output them along in the JSON file or to the MongoDB server.
|
||||
By default, VMTP is not able to get the Linux distro nor the OpenStack version of the cloud deployment under test.
|
||||
However, by providing the credentials of the controller node under test, VMTP will try to fetch these information, and output them along in the JSON file or to the MongoDB server.
|
||||
For example to retrieve the OpenStack distribution information on a given controller node:
|
||||
|
||||
.. code:
|
||||
python vmtp.py --json tb172.json --test-description 'Testbed 172' --controller-node root@172.22.191.172
|
||||
|
||||
Bandwidth Limit for TCP/UDP Flow Measurements
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -179,9 +183,10 @@ Create one configuration file for your specific cloud and use the *-c* option to
|
||||
|
||||
Upload the Linux image to the OpenStack controller node, so that OpenStack is able to spawning VMs. You will be prompted an error if the image defined in the config file is not available to use when running the tool. The image can be uploaded using either Horizon dashboard, or the command below::
|
||||
|
||||
.. code::
|
||||
python vmtp.py -r admin-openrc.sh -p admin --vm_image_url http://<url_to_the_image>
|
||||
|
||||
**Note:** Currently, VMTP only supports the Linux image in qcow2 format.
|
||||
**Note:** Currently, VMTP only supports the qcow2 format.
|
||||
|
||||
|
||||
Examples of running VMTP on an OpenStack Cloud
|
||||
@ -192,6 +197,7 @@ Example 1: Typical Run
|
||||
|
||||
Run VMTP on an OpenStack cloud with the default configuration file, use "admin-openrc.sh" as the rc file, and "admin" as the password::
|
||||
|
||||
.. code::
|
||||
python vmtp.py -r admin-openrc.sh -p admin
|
||||
|
||||
This will generate 6 standard sets of performance data:
|
||||
@ -214,22 +220,26 @@ Example 2: Cloud upload/download performance measurement
|
||||
|
||||
Run VMTP on an OpenStack cloud with a specified configuration file (mycfg.yaml), and saved the result to a JSON file::
|
||||
|
||||
.. code::
|
||||
python vmtp.py -c mycfg.yaml -r admin-openrc.sh -p admin --external_host localadmin@172.29.87.29 --json res.json
|
||||
|
||||
This run will generate 8 sets of performance data, the standard 6 sets mentioned above, plus two sets of upload/download performance data for both TCP and UDP.
|
||||
If you do not have ssh password-less access to the external host (public key) you must specify a password:
|
||||
|
||||
**Note:** In order to perform the upload/download performance test, an external server must be specified and configured with SSH password-less access. See below for more info.
|
||||
|
||||
.. code::
|
||||
python vmtp.py -c mycfg.yaml -r admin-openrc.sh -p admin --external_host localadmin@172.29.87.29:secret --json res.json
|
||||
|
||||
Example 3: Store the OpenStack deployment details
|
||||
"""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Run VMTP on an OpenStack cloud, fetch the defails of the deployment and store it to JSON file. Assume the controlloer node is on 192.168.12.34 with admin/admin::
|
||||
|
||||
.. code::
|
||||
python vmtp.py -r admin-openrc.sh -p admin --json res.json --controller-node root@192.168.12.34:admin
|
||||
|
||||
In addition, VMTP also supports to store the results to a MongoDB server::
|
||||
|
||||
.. code::
|
||||
python vmtp.py -r admin-openrc.sh -p admin --json res.json --mongod_server 172.29.87.29 --controller-node root@192.168.12.34:admin
|
||||
|
||||
Before storing info into MongoDB, some configurations are needed to change to fit in your environment. By default, VMTP will store to database "client_db" with collection name "pns_web_entry", and of course these can be changed in the configuration file. Below are the fields which are related to accessing MongoDB::
|
||||
@ -244,6 +254,7 @@ Example 4: Specify which compute nodes to spawn VMs
|
||||
|
||||
Run VMTP on an OpenStack cloud, spawn the test server VM on tme212, and the test client VM on tme210. Save the result, and perform the inter-node measurement only::
|
||||
|
||||
.. code::
|
||||
python vmtp.py -r admin-openrc.sh -p admin --inter-node-only --json res.json --hypervisor tme212 --hypervisor tme210
|
||||
|
||||
|
||||
@ -252,23 +263,25 @@ Example 5: Collect native host performance data
|
||||
|
||||
Run VMTP to get native host throughput between 172.29.87.29 and 172.29.87.30 using the localadmin ssh username and run each tcp/udp test session for 120 seconds (instead of the default 10 seconds)::
|
||||
|
||||
.. code::
|
||||
python vmtp.py --host localadmin@172.29.87.29 --host localadmin@172.29.87.30 --time 120
|
||||
|
||||
**Note:** This command requires each host to have the VMTP public key (ssh/id_rsa.pub) inserted into the ssh/authorized_keys file in the username home directory, i.e. SSH password-less access. See below for more info.
|
||||
The first IP passed (*--host*) is always the one running the server side.
|
||||
If you do not have public keys setup on these targets, you must provide a password:
|
||||
|
||||
.. code::
|
||||
python vmtp.py --host localadmin@172.29.87.29:secret --host localadmin@172.29.87.30:secret --time 120
|
||||
|
||||
It is also possible to run VMTP between pre-existing VMs that are accessible through SSH (using floating IP) if you have the corresponding private key to access them.
|
||||
|
||||
In the case of servers that have multiple NIC and IP addresses, it is possible to specify the server side listening interface name to use (if you want the client side to connect using the associated IP address)
|
||||
For example, to measure throughput between 2 hosts using the network attached to the server interface "eth5"::
|
||||
|
||||
.. code::
|
||||
python vmtp.py --host localadmin@172.29.87.29::eth5 --host localadmin@172.29.87.30
|
||||
|
||||
|
||||
Example 6: Measurement on pre-existing VMs
|
||||
""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
It is possible to run VMTP between pre-existing VMs that are accessible through SSH (using floating IP).
|
||||
|
||||
The first IP passed (*--host*) is always the one running the server side. Optionally a server side listening interface name can be passed if clients should connect using a particular server IP. For example, to measure throughput between 2 hosts using the network attached to the server interface "eth5"::
|
||||
|
||||
python vmtp.py --host localadmin@172.29.87.29:eth5 --host localadmin@172.29.87.30
|
||||
|
||||
**Note:** Prior to running, the VMTP public key must be installed on each VM.
|
||||
|
||||
Example 7: IPV6 throughput measurement
|
||||
Example 6: IPV6 throughput measurement
|
||||
""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
It is possible to use VMTP to measure throughput for IPv6
|
||||
|
45
instance.py
45
instance.py
@ -44,9 +44,8 @@ class Instance(object):
|
||||
self.config = config
|
||||
# internal network IP
|
||||
self.internal_ip = None
|
||||
self.ssh_ip = None
|
||||
self.ssh_access = sshutils.SSHAccess()
|
||||
self.ssh_ip_id = None
|
||||
self.ssh_user = config.ssh_vm_username
|
||||
self.instance = None
|
||||
self.ssh = None
|
||||
self.port = None
|
||||
@ -58,17 +57,18 @@ class Instance(object):
|
||||
self.gmond_port = int(config.gmond_svr_port)
|
||||
else:
|
||||
self.gmond_port = 0
|
||||
self.config_drive = None
|
||||
|
||||
# Setup the ssh connectivity
|
||||
# this function is only used for native hosts
|
||||
# Returns True if success
|
||||
def setup_ssh(self, ssh_ip, ssh_user):
|
||||
def setup_ssh(self, host_access):
|
||||
# used for displaying the source IP in json results
|
||||
if not self.internal_ip:
|
||||
self.internal_ip = ssh_ip
|
||||
self.ssh_ip = ssh_ip
|
||||
self.ssh_user = ssh_user
|
||||
self.ssh = sshutils.SSH(self.ssh_user, self.ssh_ip,
|
||||
key_filename=self.config.private_key_file,
|
||||
self.internal_ip = host_access.host
|
||||
self.ssh_access = host_access
|
||||
self.buginf('Setup SSH for %s@%s' % (host_access.username, host_access.host))
|
||||
self.ssh = sshutils.SSH(self.ssh_access,
|
||||
connect_retry_count=self.config.ssh_retry_count)
|
||||
return True
|
||||
|
||||
@ -76,7 +76,7 @@ class Instance(object):
|
||||
# and extract internal network IP
|
||||
# Retruns True if success, False otherwise
|
||||
def create(self, image, flavor_type,
|
||||
keypair, int_net,
|
||||
ssh_access, int_net,
|
||||
az,
|
||||
internal_network_name,
|
||||
sec_group,
|
||||
@ -108,7 +108,7 @@ class Instance(object):
|
||||
self.instance = self.comp.create_server(self.name,
|
||||
image,
|
||||
flavor_type,
|
||||
keypair,
|
||||
self.config.public_key_name,
|
||||
nics,
|
||||
sec_group,
|
||||
az,
|
||||
@ -121,11 +121,14 @@ class Instance(object):
|
||||
self.display('Server creation failed')
|
||||
return False
|
||||
|
||||
# clone the provided ssh access to pick up user name and key pair
|
||||
self.ssh_access.copy_from(ssh_access)
|
||||
|
||||
# If reusing existing management network skip the floating ip creation and association to VM
|
||||
# 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
|
||||
self.ssh_access.host = self.instance.networks[internal_network_name][0]
|
||||
self.internal_ip = self.ssh_access.host
|
||||
else:
|
||||
# Set the internal ip to the correct ip for v4 and v6
|
||||
for ip_address in self.instance.networks[internal_network_name]:
|
||||
@ -143,18 +146,18 @@ class Instance(object):
|
||||
if not fip:
|
||||
self.display('Floating ip creation failed')
|
||||
return False
|
||||
self.ssh_ip = fip['floatingip']['floating_ip_address']
|
||||
self.ssh_access.host = fip['floatingip']['floating_ip_address']
|
||||
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, ipv4_fixed_address)
|
||||
self.buginf('Floating IP %s created', self.ssh_access.host)
|
||||
self.buginf('Started - associating floating IP %s', self.ssh_access.host)
|
||||
self.instance.add_floating_ip(self.ssh_access.host, ipv4_fixed_address)
|
||||
|
||||
# extract the IP for the data network
|
||||
self.buginf('Internal network IP: %s', self.internal_ip)
|
||||
self.buginf('SSH IP: %s', self.ssh_ip)
|
||||
self.buginf('SSH IP: %s', self.ssh_access.host)
|
||||
|
||||
# create ssh session
|
||||
if not self.setup_ssh(self.ssh_ip, self.config.ssh_vm_username):
|
||||
if not self.setup_ssh(self.ssh_access):
|
||||
return False
|
||||
return True
|
||||
|
||||
@ -251,8 +254,8 @@ class Instance(object):
|
||||
scp_cmd = 'scp -i %s %s %s %s@%s:%s' % (self.config.private_key_file,
|
||||
scp_opts,
|
||||
source,
|
||||
self.ssh_user,
|
||||
self.ssh_ip,
|
||||
self.ssh_access.username,
|
||||
self.ssh_access.host,
|
||||
dest)
|
||||
self.buginf('Copying %s to target...', tool_name)
|
||||
self.buginf(scp_cmd)
|
||||
@ -304,7 +307,7 @@ class Instance(object):
|
||||
def dispose(self):
|
||||
if self.ssh_ip_id:
|
||||
self.net.delete_floating_ip(self.ssh_ip_id)
|
||||
self.buginf('Floating IP %s deleted', self.ssh_ip)
|
||||
self.buginf('Floating IP %s deleted', self.ssh_access.host)
|
||||
self.ssh_ip_id = None
|
||||
if self.instance:
|
||||
self.comp.delete_server(self.instance)
|
||||
|
@ -38,14 +38,14 @@ class PerfInstance(Instance):
|
||||
|
||||
# No args is reserved for native host server
|
||||
def create(self, image=None, flavor_type=None,
|
||||
keypair=None, nics=None, az=None,
|
||||
ssh_access=None, nics=None, az=None,
|
||||
management_network_name=None,
|
||||
sec_group=None,
|
||||
init_file_name=None):
|
||||
'''Create an instance
|
||||
:return: True on success, False on error
|
||||
'''
|
||||
rc = Instance.create(self, image, flavor_type, keypair,
|
||||
rc = Instance.create(self, image, flavor_type, ssh_access,
|
||||
nics, az,
|
||||
management_network_name,
|
||||
sec_group,
|
||||
|
27
ssh/id_rsa
27
ssh/id_rsa
@ -1,27 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAu1wjIM/GgPbbLPoyKfN+I1uSeqrF4PYcbsTcHaQ6mEF/Ufqe
|
||||
6uZVJFR1mT1ECfxCckUMM6aaf5ESNAfxEjE9Hrs/Yd3Qw5hwSlG3HJ4uZg79m1yg
|
||||
ifXfnkp4rGNI6sqZxgyMbeQW3KoDaQ6zOe7e/KIkxX4XzR8I4d5lRx4ofIb806AB
|
||||
QJkDrq48dHLGZnOBPvpNImpg5u6EWHGa4HI4Dl2pdyEQXTXOErupalOC1Cr8oxwu
|
||||
fimSxftnl9Nh94wQtTQADnCE2GBaMMxS/ClHtJLDfmtnVC51Y4F7Ux9F3MSTSRBP
|
||||
gNxcd9OikMKSj6RNq/PHw5+9R0h5v2lJXvalCQIDAQABAoIBAArCu/HCfTAi/WuT
|
||||
4xWtumzlcYBCFqNY/0ENZWb+a68a8+kNb9sl53Xys95dOm8oYdiWRqEgzHbPKjB6
|
||||
1EmrMkt1japdRwQ02R4rm0y1eQy7h61IoJ/L01AQDuY3vZReln5dciNNmlKKITAD
|
||||
fB+zrHLuDRaaq1tIkQYH8+ElxkWAkd/vRQC4FP1OMIGnX4TdQ8lcG2DxwMs5jqJ6
|
||||
ufTeR6QMDEymNYQwcdFhe5wNi57IEbN9B+N95yaktWsYV34HuYV2ndZtrhMLFhcq
|
||||
Psw3vgrXBrreVPZ/iX1zeWgrjJb1AVOCtsOZ+O4YfZIIBWnhjj9sJnDCpMWmioH5
|
||||
a0UmF0ECgYEA+NyIS5MmbrVJKVqWUJubSbaZXQEW3Jv4njRFAyG7NVapSbllF5t2
|
||||
lq5usUI+l1XaZ3v6IpYPG+K+U1Ggo3+E6RFEDwVrZF0NYLOPXBydhkFFB4nHpTSX
|
||||
uBo65/SiMDSassrqs/PFCDdsiUQ87sMFp+gouDePcBDC1OyHRDxR220CgYEAwLv6
|
||||
zvqi5AvzO+tZQHRHcQdvCNC436KsUZlV6jQ5j3tUlqXlLRl8bWfih7Hu2uBGNjy2
|
||||
Fao517Nd/kBdjVaechn/fvmLwgflQso0q1j63u2nZ32uYTd+zLnW1yJM4UCs/Hqb
|
||||
hebRYDeZuRfobp62lEl6rdGij5LLRzQClOArso0CgYAaHClApKO3odWXPSXgNzNH
|
||||
vJzCoUagxsyC7MEA3x0hL4J7dbQhkfITRSHf/y9J+Xv8t4k677uOFXAalcng3ZQ4
|
||||
T9NwMAVgdlLc/nngFDCC0X5ImDAWKTpx2m6rv4L0w9AnShrt3nmhrw74J+yssFF7
|
||||
mGQNT+cAvwFyDY7zndCI0QKBgEkZw0on5ApsweezHxoEQGiNcj68s7IWyBb2+pAn
|
||||
GMHj/DRbXa4aYYg5g8EF6ttXfynpIwLamq/GV1ss3I7UEKqkU7S8P5brWbhYa1um
|
||||
FxjguMLW94HmA5Dw15ynZNN2rWXhtwU1g6pjzElY2Q7D4eoiaIZu4aJlAfbSsjv3
|
||||
PnutAoGBAMBRX8BbFODtQr68c6LWWda5zQ+kNgeCv+2ejG6rsEQ+Lxwi86Oc6udG
|
||||
kTP4xuZo80MEW/t+kibFgU6gm1WTVltpbjo0XTaHE1OV4JeNC8edYFTi1DVO5r1M
|
||||
ch+pkN20FQmZ+cLLn6nOeTJ6/9KXWKAZMPZ4SH4BnmF7iEa7yc8f
|
||||
-----END RSA PRIVATE KEY-----
|
@ -1 +0,0 @@
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7XCMgz8aA9tss+jIp834jW5J6qsXg9hxuxNwdpDqYQX9R+p7q5lUkVHWZPUQJ/EJyRQwzppp/kRI0B/ESMT0euz9h3dDDmHBKUbccni5mDv2bXKCJ9d+eSnisY0jqypnGDIxt5BbcqgNpDrM57t78oiTFfhfNHwjh3mVHHih8hvzToAFAmQOurjx0csZmc4E++k0iamDm7oRYcZrgcjgOXal3IRBdNc4Su6lqU4LUKvyjHC5+KZLF+2eX02H3jBC1NAAOcITYYFowzFL8KUe0ksN+a2dULnVjgXtTH0XcxJNJEE+A3Fx306KQwpKPpE2r88fDn71HSHm/aUle9qUJ openstack-pns
|
109
sshutils.py
109
sshutils.py
@ -61,6 +61,7 @@ import re
|
||||
import select
|
||||
import socket
|
||||
import StringIO
|
||||
import sys
|
||||
import time
|
||||
|
||||
import paramiko
|
||||
@ -76,12 +77,70 @@ class SSHError(Exception):
|
||||
class SSHTimeout(SSHError):
|
||||
pass
|
||||
|
||||
# Check IPv4 address syntax - not completely fool proof but will catch
|
||||
# some invalid formats
|
||||
def is_ipv4(address):
|
||||
try:
|
||||
socket.inet_aton(address)
|
||||
except socket.error:
|
||||
return False
|
||||
return True
|
||||
|
||||
class SSHAccess(object):
|
||||
'''
|
||||
A class to contain all the information needed to access a host
|
||||
(native or virtual) using SSH
|
||||
'''
|
||||
def __init__(self, arg_value=None):
|
||||
'''
|
||||
decode user@host[:pwd]
|
||||
'hugo@1.1.1.1:secret' -> ('hugo', '1.1.1.1', 'secret', None)
|
||||
'huggy@2.2.2.2' -> ('huggy', '2.2.2.2', None, None)
|
||||
None ->(None, None, None, None)
|
||||
Examples of fatal errors (will call exit):
|
||||
'hutch@q.1.1.1' (invalid IP)
|
||||
'@3.3.3.3' (missing username)
|
||||
'hiro@' or 'buggy' (missing host IP)
|
||||
The error field will be None in case of success or will
|
||||
contain a string describing the error
|
||||
'''
|
||||
self.username = None
|
||||
self.host = None
|
||||
self.password = None
|
||||
# name of the file that contains the private key
|
||||
self.private_key_file = None
|
||||
# this is the private key itself (a long string starting with
|
||||
# -----BEGIN RSA PRIVATE KEY-----
|
||||
# used when the private key is not saved in any file
|
||||
self.private_key = None
|
||||
self.public_key_file = None
|
||||
self.port = 22
|
||||
self.error = None
|
||||
|
||||
if not arg_value:
|
||||
return
|
||||
match = re.search(r'^([^@]+)@([0-9\.]+):?(.*)$', arg_value)
|
||||
if not match:
|
||||
self.error = 'Invalid argument: ' + arg_value
|
||||
return
|
||||
if not is_ipv4(match.group(2)):
|
||||
self.error = 'Invalid IPv4 address ' + match.group(2)
|
||||
return
|
||||
(self.username, self.host, self.password) = match.groups()
|
||||
|
||||
def copy_from(self, ssh_access):
|
||||
self.username = ssh_access.username
|
||||
self.host = ssh_access.host
|
||||
self.port = ssh_access.port
|
||||
self.password = ssh_access.password
|
||||
self.private_key = ssh_access.private_key
|
||||
self.public_key_file = ssh_access.public_key_file
|
||||
self.private_key_file = ssh_access.private_key_file
|
||||
|
||||
class SSH(object):
|
||||
"""Represent ssh connection."""
|
||||
|
||||
def __init__(self, user, host, port=22, pkey=None,
|
||||
key_filename=None, password=None,
|
||||
def __init__(self, ssh_access,
|
||||
connect_timeout=60,
|
||||
connect_retry_count=30,
|
||||
connect_retry_wait_sec=2):
|
||||
@ -98,12 +157,11 @@ class SSH(object):
|
||||
:param connect_retry_wait_sec: seconds to wait between retries
|
||||
"""
|
||||
|
||||
self.user = user
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.pkey = self._get_pkey(pkey) if pkey else None
|
||||
self.password = password
|
||||
self.key_filename = key_filename
|
||||
self.ssh_access = ssh_access
|
||||
if ssh_access.private_key:
|
||||
self.pkey = self._get_pkey(ssh_access.private_key)
|
||||
else:
|
||||
self.pkey = None
|
||||
self._client = False
|
||||
self.connect_timeout = connect_timeout
|
||||
self.connect_retry_count = connect_retry_count
|
||||
@ -114,6 +172,9 @@ class SSH(object):
|
||||
self.__get_distro()
|
||||
|
||||
def _get_pkey(self, key):
|
||||
'''Get the binary form of the private key
|
||||
from the text form
|
||||
'''
|
||||
if isinstance(key, basestring):
|
||||
key = StringIO.StringIO(key)
|
||||
errors = []
|
||||
@ -131,10 +192,12 @@ class SSH(object):
|
||||
self._client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
for _ in range(self.connect_retry_count):
|
||||
try:
|
||||
self._client.connect(self.host, username=self.user,
|
||||
port=self.port, pkey=self.pkey,
|
||||
key_filename=self.key_filename,
|
||||
password=self.password,
|
||||
self._client.connect(self.ssh_access.host,
|
||||
username=self.ssh_access.username,
|
||||
port=self.ssh_access.port,
|
||||
pkey=self.pkey,
|
||||
key_filename=self.ssh_access.private_key_file,
|
||||
password=self.ssh_access.password,
|
||||
timeout=self.connect_timeout)
|
||||
return self._client
|
||||
except (paramiko.AuthenticationException,
|
||||
@ -144,7 +207,7 @@ class SSH(object):
|
||||
time.sleep(self.connect_retry_wait_sec)
|
||||
|
||||
self._client = None
|
||||
msg = '[%s] SSH Connection failed after %s attempts' % (self.host,
|
||||
msg = '[%s] SSH Connection failed after %s attempts' % (self.ssh_access.host,
|
||||
self.connect_retry_count)
|
||||
raise SSHError(msg)
|
||||
|
||||
@ -225,7 +288,7 @@ class SSH(object):
|
||||
break
|
||||
|
||||
if timeout and (time.time() - timeout) > start_time:
|
||||
args = {'cmd': cmd, 'host': self.host}
|
||||
args = {'cmd': cmd, 'host': self.ssh_access.host}
|
||||
raise SSHTimeout(('Timeout executing command '
|
||||
'"%(cmd)s" on host %(host)s') % args)
|
||||
# if e:
|
||||
@ -269,7 +332,7 @@ class SSH(object):
|
||||
except (socket.error, SSHError):
|
||||
time.sleep(interval)
|
||||
if time.time() > (start_time + timeout):
|
||||
raise SSHTimeout(('Timeout waiting for "%s"') % self.host)
|
||||
raise SSHTimeout(('Timeout waiting for "%s"') % self.ssh_access.host)
|
||||
|
||||
def __extract_property(self, name, input_str):
|
||||
expr = name + r'="?([\w\.]*)"?'
|
||||
@ -587,19 +650,19 @@ class SSH(object):
|
||||
# invoked from pns script.
|
||||
##################################################
|
||||
def main():
|
||||
# ssh = SSH('localadmin', '172.29.87.29', key_filename='./ssh/id_rsa')
|
||||
ssh = SSH('localadmin', '172.22.191.173', key_filename='./ssh/id_rsa')
|
||||
# As argument pass the SSH access string, e.g. "localadmin@1.1.1.1:secret"
|
||||
test_ssh = SSH(SSHAccess(sys.argv[1]))
|
||||
|
||||
print 'ID=' + ssh.distro_id
|
||||
print 'ID_LIKE=' + ssh.distro_id_like
|
||||
print 'VERSION_ID=' + ssh.distro_version
|
||||
print 'ID=' + test_ssh.distro_id
|
||||
print 'ID_LIKE=' + test_ssh.distro_id_like
|
||||
print 'VERSION_ID=' + test_ssh.distro_version
|
||||
|
||||
# ssh.wait()
|
||||
# print ssh.pidof('bash')
|
||||
# print ssh.stat('/tmp')
|
||||
print ssh.check_openstack_version()
|
||||
print ssh.get_cpu_info()
|
||||
print ssh.get_l2agent_version("Open vSwitch agent")
|
||||
print test_ssh.check_openstack_version()
|
||||
print test_ssh.get_cpu_info()
|
||||
print test_ssh.get_l2agent_version("Open vSwitch agent")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
293
vmtp.py
293
vmtp.py
@ -20,8 +20,6 @@ import json
|
||||
import os
|
||||
import pprint
|
||||
import re
|
||||
import socket
|
||||
import stat
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
@ -40,23 +38,14 @@ from neutronclient.v2_0 import client as neutronclient
|
||||
from novaclient.client import Client
|
||||
from novaclient.exceptions import ClientException
|
||||
|
||||
__version__ = '2.0.4'
|
||||
__version__ = '2.1.0'
|
||||
|
||||
from perf_instance import PerfInstance as PerfInstance
|
||||
|
||||
# Check IPv4 address syntax - not completely fool proof but will catch
|
||||
# some invalid formats
|
||||
def is_ipv4(address):
|
||||
try:
|
||||
socket.inet_aton(address)
|
||||
except socket.error:
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_absolute_path_for_file(file_name):
|
||||
def get_vmtp_absolute_path_for_file(file_name):
|
||||
'''
|
||||
Return the filename in absolute path for any file
|
||||
passed as relateive path.
|
||||
passed as relative path to the vmtp directory
|
||||
'''
|
||||
if os.path.isabs(__file__):
|
||||
abs_file_path = os.path.join(__file__.split("vmtp.py")[0],
|
||||
@ -73,10 +62,16 @@ def normalize_paths(cfg):
|
||||
'''
|
||||
Normalize the various paths to config files, tools, ssh priv and pub key
|
||||
files.
|
||||
If a relative path is entered:
|
||||
- the key pair file names are relative to the current directory
|
||||
- the perftool path is relative to vmtp itself
|
||||
'''
|
||||
cfg.public_key_file = get_absolute_path_for_file(cfg.public_key_file)
|
||||
cfg.private_key_file = get_absolute_path_for_file(cfg.private_key_file)
|
||||
cfg.perf_tool_path = get_absolute_path_for_file(cfg.perf_tool_path)
|
||||
if cfg.public_key_file:
|
||||
cfg.public_key_file = os.path.abspath(os.path.expanduser(cfg.public_key_file))
|
||||
if cfg.private_key_file:
|
||||
cfg.private_key_file = os.path.expanduser(os.path.expanduser(cfg.private_key_file))
|
||||
if cfg.perf_tool_path:
|
||||
cfg.perf_tool_path = get_vmtp_absolute_path_for_file(cfg.perf_tool_path)
|
||||
|
||||
class FlowPrinter(object):
|
||||
|
||||
@ -100,6 +95,9 @@ class ResultsCollector(object):
|
||||
def add_property(self, name, value):
|
||||
self.results[name] = value
|
||||
|
||||
def add_properties(self, properties):
|
||||
self.results.update(properties)
|
||||
|
||||
def add_flow_result(self, flow_res):
|
||||
self.results['flows'].append(flow_res)
|
||||
self.ppr.pprint(flow_res)
|
||||
@ -110,45 +108,23 @@ class ResultsCollector(object):
|
||||
def pprint(self, res):
|
||||
self.ppr.pprint(res)
|
||||
|
||||
def get_controller_info(self, cfg, net):
|
||||
if cfg.ctrl_username and cfg.ctrl_host:
|
||||
print 'Fetching OpenStack deployment details...'
|
||||
if cfg.ctrl_password:
|
||||
sshcon = sshutils.SSH(cfg.ctrl_username,
|
||||
cfg.ctrl_host,
|
||||
password=cfg.ctrl_password)
|
||||
else:
|
||||
sshcon = sshutils.SSH(cfg.ctrl_username,
|
||||
cfg.ctrl_host,
|
||||
key_filename=cfg.private_key_file,
|
||||
connect_retry_count=cfg.ssh_retry_count)
|
||||
if sshcon is not None:
|
||||
self.results['distro'] = sshcon.get_host_os_version()
|
||||
self.results['openstack_version'] = sshcon.check_openstack_version()
|
||||
self.results['cpu_info'] = sshcon.get_cpu_info()
|
||||
if 'l2agent_type' in self.results and 'encapsulation' in self.results:
|
||||
self.results['nic_name'] = sshcon.get_nic_name(
|
||||
self.results['l2agent_type'], self.results['encapsulation'],
|
||||
net.internal_iface_dict)
|
||||
self.results['l2agent_version'] = sshcon.get_l2agent_version(
|
||||
self.results['l2agent_type'])
|
||||
else:
|
||||
self.results['nic_name'] = "Unknown"
|
||||
else:
|
||||
print 'ERROR: Cannot connect to the controller node.'
|
||||
def get_result(self, key):
|
||||
if keystoneclient in self.results:
|
||||
return self.results[key]
|
||||
return None
|
||||
|
||||
def mask_credentials(self):
|
||||
args = self.results['args']
|
||||
if not args:
|
||||
arguments = self.results['args']
|
||||
if not arguments:
|
||||
return
|
||||
|
||||
list = ['-p', '--host', '--external-host', '--controller-node']
|
||||
for keyword in list:
|
||||
arg_list = ['-p', '--host', '--external-host', '--controller-node']
|
||||
for keyword in arg_list:
|
||||
pattern = keyword + r'\s+[^\s]+'
|
||||
string = keyword + ' <MASKED>'
|
||||
args = re.sub(pattern, string, args)
|
||||
arguments = re.sub(pattern, string, arguments)
|
||||
|
||||
self.results['args'] = args
|
||||
self.results['args'] = arguments
|
||||
|
||||
def generate_runid(self):
|
||||
key = self.results['args'] + self.results['date'] + self.results['version']
|
||||
@ -203,7 +179,7 @@ class VmtpTest(object):
|
||||
def create_instance(self, inst, az, int_net):
|
||||
self.assert_true(inst.create(self.image_instance,
|
||||
self.flavor_type,
|
||||
config.public_key_name,
|
||||
instance_access,
|
||||
int_net,
|
||||
az,
|
||||
int_net['name'],
|
||||
@ -223,9 +199,9 @@ class VmtpTest(object):
|
||||
neutron = neutronclient.Client(**creds)
|
||||
|
||||
self.comp = compute.Compute(nova_client, config)
|
||||
# Add the script public key to openstack
|
||||
self.comp.add_public_key(config.public_key_name,
|
||||
config.public_key_file)
|
||||
|
||||
# Add the appropriate public key to openstack
|
||||
self.comp.init_key_pair(config.public_key_name, instance_access)
|
||||
|
||||
self.image_instance = self.comp.find_image(config.image_name)
|
||||
if self.image_instance is None:
|
||||
@ -269,13 +245,13 @@ class VmtpTest(object):
|
||||
self.server.internal_ip = config.vm_server_internal_ip
|
||||
self.client.internal_ip = config.vm_client_internal_ip
|
||||
if config.vm_server_external_ip:
|
||||
self.server.ssh_ip = config.vm_server_external_ip
|
||||
self.server.ssh_access.host = config.vm_server_external_ip
|
||||
else:
|
||||
self.server.ssh_ip = config.vm_server_internal_ip
|
||||
self.server.ssh_access.host = config.vm_server_internal_ip
|
||||
if config.vm_client_external_ip:
|
||||
self.client.ssh_ip = config.vm_client_external_ip
|
||||
self.client.ssh_access.host = config.vm_client_external_ip
|
||||
else:
|
||||
self.client.ssh_ip = config.vm_client_internal_ip
|
||||
self.client.ssh_access.host = config.vm_client_internal_ip
|
||||
return
|
||||
|
||||
# this is the standard way of running the test
|
||||
@ -318,15 +294,15 @@ class VmtpTest(object):
|
||||
|
||||
# Test throughput for the case of the external host
|
||||
def ext_host_tp_test(self):
|
||||
client = PerfInstance('Host-' + ext_host_list[1] + '-Client', config)
|
||||
if not client.setup_ssh(ext_host_list[1], ext_host_list[0]):
|
||||
client.display('SSH failed, check IP or make sure public key is configured')
|
||||
client = PerfInstance('Host-' + config.ext_host.host + '-Client', config)
|
||||
if not client.setup_ssh(config.ext_host):
|
||||
client.display('SSH to ext host failed, check IP or make sure public key is configured')
|
||||
else:
|
||||
client.buginf('SSH connected')
|
||||
client.create()
|
||||
fpr.print_desc('External-VM (upload/download)')
|
||||
res = client.run_client('External-VM',
|
||||
self.server.ssh_ip,
|
||||
self.server.ssh_access.host,
|
||||
self.server,
|
||||
bandwidth=config.vm_bandwidth,
|
||||
bidirectional=True)
|
||||
@ -392,13 +368,13 @@ class VmtpTest(object):
|
||||
self.server.internal_ip)
|
||||
if not config.ipv6_mode:
|
||||
self.measure_flow("VM to VM different network floating IP",
|
||||
self.server.ssh_ip)
|
||||
self.server.ssh_access.host)
|
||||
|
||||
self.client.dispose()
|
||||
self.client = None
|
||||
|
||||
# If external network is specified run that case
|
||||
if ext_host_list[0]:
|
||||
if config.ext_host:
|
||||
self.ext_host_tp_test()
|
||||
|
||||
def teardown(self):
|
||||
@ -442,12 +418,12 @@ class VmtpTest(object):
|
||||
else:
|
||||
self.teardown()
|
||||
|
||||
def test_native_tp(nhosts):
|
||||
def test_native_tp(nhosts, ifname):
|
||||
fpr.print_desc('Native Host to Host throughput')
|
||||
server_host = nhosts[0]
|
||||
server = PerfInstance('Host-' + server_host[1] + '-Server', config, server=True)
|
||||
server = PerfInstance('Host-' + server_host.host + '-Server', config, server=True)
|
||||
|
||||
if not server.setup_ssh(server_host[1], server_host[0]):
|
||||
if not server.setup_ssh(server_host):
|
||||
server.display('SSH failed, check IP or make sure public key is configured')
|
||||
else:
|
||||
server.display('SSH connected')
|
||||
@ -460,23 +436,23 @@ def test_native_tp(nhosts):
|
||||
nhosts.pop(0)
|
||||
# IP address clients should connect to, check if the user
|
||||
# has passed a server listen interface name
|
||||
if len(server_host) == 3:
|
||||
if ifname:
|
||||
# use the IP address configured on given interface
|
||||
server_ip = server.get_interface_ip(server_host[2])
|
||||
server_ip = server.get_interface_ip(ifname)
|
||||
if not server_ip:
|
||||
print('Error: cannot get IP address for interface ' + server_host[2])
|
||||
print('Error: cannot get IP address for interface ' + ifname)
|
||||
else:
|
||||
server.display('Clients will use server IP address %s (%s)' %
|
||||
(server_ip, server_host[2]))
|
||||
(server_ip, ifname))
|
||||
else:
|
||||
# use same as ssh IP
|
||||
server_ip = server_host[1]
|
||||
server_ip = server_host.host
|
||||
|
||||
if server_ip:
|
||||
# start client side, 1 per host provided
|
||||
for client_host in nhosts:
|
||||
client = PerfInstance('Host-' + client_host[1] + '-Client', config)
|
||||
if not client.setup_ssh(client_host[1], client_host[0]):
|
||||
client = PerfInstance('Host-' + client_host.host + '-Client', config)
|
||||
if not client.setup_ssh(client_host):
|
||||
client.display('SSH failed, check IP or make sure public key is configured')
|
||||
else:
|
||||
client.buginf('SSH connected')
|
||||
@ -489,35 +465,30 @@ def test_native_tp(nhosts):
|
||||
client.dispose()
|
||||
server.dispose()
|
||||
|
||||
def extract_user_host_pwd(user_host_pwd):
|
||||
def _get_ssh_access(opt_name, opt_value):
|
||||
'''Allocate a HostSshAccess instance to the option value
|
||||
Check that a password is provided or the key pair in the config file
|
||||
is valid.
|
||||
If invalid exit with proper error message
|
||||
'''
|
||||
splits user@host[:pwd] into a 3 element tuple
|
||||
'hugo@1.1.1.1:secret' -> ('hugo', '1.1.1.1', 'secret')
|
||||
'huggy@2.2.2.2' -> ('huggy', '2.2.2.2', None)
|
||||
None ->(None, None, None)
|
||||
Examples of fatal errors (will call exit):
|
||||
'hutch@q.1.1.1' (invalid IP)
|
||||
'@3.3.3.3' (missing username)
|
||||
'hiro@' or 'buggy' (missing host IP)
|
||||
'''
|
||||
if not user_host_pwd:
|
||||
return (None, None, None)
|
||||
match = re.search(r'^([^@]+)@([0-9\.]+):?(.*)$', user_host_pwd)
|
||||
if not match:
|
||||
print('Invalid argument: ' + user_host_pwd)
|
||||
sys.exit(3)
|
||||
if not is_ipv4(match.group(2)):
|
||||
print 'Invalid IPv4 address ' + match.group(2)
|
||||
sys.exit(4)
|
||||
return match.groups()
|
||||
if not opt_value:
|
||||
return None
|
||||
|
||||
def _merge_config(file, source_config, required=False):
|
||||
host_access = sshutils.SSHAccess(opt_value)
|
||||
host_access.private_key_file = config.private_key_file
|
||||
host_access.public_key_file = config.public_key_file
|
||||
if host_access.error:
|
||||
print'Error for --' + (opt_name + ':' + host_access.error)
|
||||
sys.exit(2)
|
||||
return host_access
|
||||
|
||||
def _merge_config(cfg_file, source_config, required=False):
|
||||
'''
|
||||
returns the merged config or exits if the file does not exist and is required
|
||||
'''
|
||||
dest_config = source_config
|
||||
|
||||
fullname = os.path.expanduser(file)
|
||||
fullname = os.path.expanduser(cfg_file)
|
||||
if os.path.isfile(fullname):
|
||||
print('Loading ' + fullname + '...')
|
||||
try:
|
||||
@ -535,6 +506,32 @@ def _merge_config(file, source_config, required=False):
|
||||
sys.exit(1)
|
||||
return dest_config
|
||||
|
||||
def get_controller_info(ssh_access, net, res_col):
|
||||
if not ssh_access:
|
||||
return
|
||||
print 'Fetching OpenStack deployment details...'
|
||||
sshcon = sshutils.SSH(ssh_access,
|
||||
connect_retry_count=config.ssh_retry_count)
|
||||
if sshcon is None:
|
||||
print 'ERROR: Cannot connect to the controller node'
|
||||
return
|
||||
res = {}
|
||||
res['distro'] = sshcon.get_host_os_version()
|
||||
res['openstack_version'] = sshcon.check_openstack_version()
|
||||
res['cpu_info'] = sshcon.get_cpu_info()
|
||||
if net:
|
||||
l2type = res_col.get_result('l2agent_type')
|
||||
encap = res_col.get_result('encapsulation')
|
||||
if l2type:
|
||||
if encap:
|
||||
res['nic_name'] = sshcon.get_nic_name(l2type, encap,
|
||||
net.internal_iface_dict)
|
||||
res['l2agent_version'] = sshcon.get_l2agent_version(l2type)
|
||||
# print results
|
||||
res_col.pprint(res)
|
||||
res_col.add_properties(res)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
fpr = FlowPrinter()
|
||||
@ -569,20 +566,20 @@ if __name__ == '__main__':
|
||||
|
||||
parser.add_argument('--host', dest='hosts',
|
||||
action='append',
|
||||
help='native host throughput (targets requires ssh key)',
|
||||
metavar='<user>@<host_ssh_ip>[:<server-listen-if-name>]')
|
||||
help='native host throughput (password or public key required)',
|
||||
metavar='<user>@<host_ssh_ip>[:<password>:<server-listen-if-name>]')
|
||||
|
||||
parser.add_argument('--external-host', dest='ext_host',
|
||||
action='store',
|
||||
help='external-VM throughput (host requires public key if no password)',
|
||||
help='external-VM throughput (password or public key required)',
|
||||
metavar='<user>@<host_ssh_ip>[:password>]')
|
||||
|
||||
parser.add_argument('--controller-node', dest='controller_node',
|
||||
action='store',
|
||||
help='controller node ssh (host requires public key if no password)',
|
||||
help='controller node ssh (password or public key required)',
|
||||
metavar='<user>@<host_ssh_ip>[:<password>]')
|
||||
|
||||
parser.add_argument('--mongod_server', dest='mongod_server',
|
||||
parser.add_argument('--mongod-server', dest='mongod_server',
|
||||
action='store',
|
||||
help='provide mongoDB server IP to store results',
|
||||
metavar='<server ip>')
|
||||
@ -663,12 +660,12 @@ if __name__ == '__main__':
|
||||
action='store_true',
|
||||
help='Stop and keep everything as-is on error (must cleanup manually)')
|
||||
|
||||
parser.add_argument('--vm_image_url', dest='vm_image_url',
|
||||
parser.add_argument('--vm-image-url', dest='vm_image_url',
|
||||
action='store',
|
||||
help='URL to a Linux image in qcow2 format that can be downloaded from',
|
||||
metavar='<url_to_image>')
|
||||
|
||||
parser.add_argument('--test_description', dest='test_description',
|
||||
parser.add_argument('--test-description', dest='test_description',
|
||||
action='store',
|
||||
help='The test description to be stored in JSON or MongoDB',
|
||||
metavar='<test_description>')
|
||||
@ -676,7 +673,7 @@ if __name__ == '__main__':
|
||||
|
||||
(opts, args) = parser.parse_known_args()
|
||||
|
||||
default_cfg_file = get_absolute_path_for_file("cfg.default.yaml")
|
||||
default_cfg_file = get_vmtp_absolute_path_for_file("cfg.default.yaml")
|
||||
|
||||
# read the default configuration file and possibly an override config file
|
||||
# the precedence order is as follows:
|
||||
@ -697,6 +694,13 @@ if __name__ == '__main__':
|
||||
config.debug = opts.debug
|
||||
config.inter_node_only = opts.inter_node_only
|
||||
|
||||
if config.public_key_file and not os.path.isfile(config.public_key_file):
|
||||
print('Warning: invalid public_key_file:' + config.public_key_file)
|
||||
config.public_key_file = None
|
||||
if config.private_key_file and not os.path.isfile(config.private_key_file):
|
||||
print('Warning: invalid private_key_file:' + config.private_key_file)
|
||||
config.private_key_file = None
|
||||
|
||||
# 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)
|
||||
@ -716,14 +720,31 @@ if __name__ == '__main__':
|
||||
else:
|
||||
config.json_file = None
|
||||
|
||||
###################################################
|
||||
# controller node ssh access to collect metadata for
|
||||
# the run.
|
||||
###################################################
|
||||
(config.ctrl_username, config.ctrl_host, config.ctrl_password) = \
|
||||
extract_user_host_pwd(opts.controller_node)
|
||||
# Add the external host info to a list
|
||||
ext_host_list = list(extract_user_host_pwd(opts.ext_host))
|
||||
# Initialize the external host access
|
||||
config.ext_host = _get_ssh_access('external-host', opts.ext_host)
|
||||
|
||||
# This is a template host access that will be used for all instances
|
||||
# (the only specific field specific to each instance is the host IP)
|
||||
# For test VM access, we never use password and always need a key pair
|
||||
instance_access = sshutils.SSHAccess()
|
||||
instance_access.username = config.ssh_vm_username
|
||||
# if the configuration does not have a
|
||||
# key pair specified, we check if the user has a personal key pair
|
||||
# if no key pair is configured or usable, a temporary key pair will be created
|
||||
if config.public_key_file and config.private_key_file:
|
||||
instance_access.public_key_file = config.public_key_file
|
||||
instance_access.private_key_file = config.private_key_file
|
||||
else:
|
||||
pub_key = os.path.expanduser('~/.ssh/id_rsa.pub')
|
||||
priv_key = os.path.expanduser('~/.ssh/id_rsa')
|
||||
if os.path.isfile(pub_key) and os.path.isfile(priv_key):
|
||||
instance_access.public_key_file = pub_key
|
||||
instance_access.private_key_file = priv_key
|
||||
|
||||
if opts.debug and instance_access.public_key_file:
|
||||
print('VM public key: ' + instance_access.public_key_file)
|
||||
print('VM private key: ' + instance_access.private_key_file)
|
||||
|
||||
|
||||
###################################################
|
||||
# VM Image URL
|
||||
@ -812,10 +833,6 @@ if __name__ == '__main__':
|
||||
###################################################
|
||||
normalize_paths(config)
|
||||
|
||||
# first chmod the local private key since git does not keep the permission
|
||||
# as this is required by ssh/scp
|
||||
os.chmod(config.private_key_file, stat.S_IRUSR | stat.S_IWUSR)
|
||||
|
||||
# Check the tp-tool name
|
||||
config.protocols = opts.protocols.upper()
|
||||
if 'T' in config.protocols or 'U' in config.protocols:
|
||||
@ -829,31 +846,24 @@ if __name__ == '__main__':
|
||||
else:
|
||||
config.tp_tool = None
|
||||
|
||||
# 3 forms are accepted:
|
||||
# --host 1.1.1.1
|
||||
# --host root@1.1.1.1
|
||||
# --host root@1.1.1.1:eth0
|
||||
# A list of 0 to 2 lists where each nested list is
|
||||
# a list of 1 to 3 elements. e.g.:
|
||||
# [['ubuntu','1.1.1.1'],['root', 2.2.2.2]]
|
||||
# [['ubuntu','1.1.1.1', 'eth0'],['root', 2.2.2.2]]
|
||||
# when not provided the default user is 'root'
|
||||
# 3 forms
|
||||
# A list of 0 to 2 HostSshAccess elements
|
||||
if opts.hosts:
|
||||
native_hosts = []
|
||||
if_name = None
|
||||
for host in opts.hosts:
|
||||
# split on '@' first
|
||||
elem_list = host.split("@")
|
||||
if len(elem_list) == 1:
|
||||
elem_list.insert(0, 'root')
|
||||
# split out the if name if present
|
||||
# ['root':'1.1.1.1:eth0'] becomes ['root':'1.1.1.1', 'eth0']
|
||||
if ':' in elem_list[1]:
|
||||
elem_list.extend(elem_list.pop().split(':'))
|
||||
if not is_ipv4(elem_list[1]):
|
||||
print 'Invalid IPv4 address ' + elem_list[1]
|
||||
sys.exit(1)
|
||||
native_hosts.append(elem_list)
|
||||
test_native_tp(native_hosts)
|
||||
# decode and extract the trailing if name first
|
||||
# there is an if name if there are at least 2 ':' in the argument
|
||||
# e.g. "root@1.1.1.1:secret:eth0"
|
||||
if host.count(':') >= 2:
|
||||
last_column_index = host.rfind(':')
|
||||
# can be empty
|
||||
last_arg = host[last_column_index + 1:]
|
||||
if not if_name and last_arg:
|
||||
if_name = last_arg
|
||||
host = host[:last_column_index]
|
||||
native_hosts.append(_get_ssh_access('host', host))
|
||||
test_native_tp(native_hosts, if_name)
|
||||
|
||||
cred = credentials.Credentials(opts.rc, opts.pwd, opts.no_env)
|
||||
|
||||
@ -861,17 +871,22 @@ if __name__ == '__main__':
|
||||
# those args that have not been parsed by this parser so that the
|
||||
# unit test parser is not bothered by local arguments
|
||||
sys.argv[1:] = args
|
||||
|
||||
vmtp_net = None
|
||||
if cred.rc_auth_url:
|
||||
if opts.debug:
|
||||
print 'Using ' + cred.rc_auth_url
|
||||
rescol.add_property('auth_url', cred.rc_auth_url)
|
||||
vmtp = VmtpTest()
|
||||
vmtp.run()
|
||||
vmtp_net = vmtp.net
|
||||
|
||||
# Retrieve controller information if requested
|
||||
# controller node ssh access to collect metadata for the run.
|
||||
ctrl_host_access = _get_ssh_access('controller-node', opts.controller_node)
|
||||
get_controller_info(ctrl_host_access, vmtp_net, rescol)
|
||||
|
||||
# If saving the results to JSON or MongoDB, get additional details:
|
||||
if config.json_file or config.vmtp_mongod_ip:
|
||||
rescol.get_controller_info(config, vmtp.net)
|
||||
rescol.mask_credentials()
|
||||
rescol.generate_runid()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user