From 90fbf3012b1ab7a581f16c61d6737dd2a0843bd1 Mon Sep 17 00:00:00 2001 From: Yichen Wang Date: Mon, 3 Aug 2015 15:40:29 -0700 Subject: [PATCH] Initial commit to openstack/kloudbuster Change-Id: Id7e009e3a9ed61e86c45c8e4839208ecfa30bd77 --- .coveragerc | 4 +- .dockerignore | 8 - .gitignore | 5 +- .mailmap | 2 +- CONTRIBUTING.rst | 17 + Dockerfile | 23 - HACKING.rst | 2 +- README.rst | 133 +-- cfg.default.yaml | 185 ---- cfg.existing.yaml | 10 - compute.py | 463 --------- credentials.py | 110 --- doc/Makefile | 8 +- doc/source/_static/example.json | 409 -------- doc/source/conf.py | 23 +- doc/source/contributing.rst | 113 +-- doc/source/images/flows.png | Bin 47456 -> 0 bytes doc/source/images/genchart-sample.png | Bin 80514 -> 0 bytes doc/source/implementation.rst | 19 - doc/source/index.rst | 21 +- doc/source/installation.rst | 117 +-- doc/source/issue.rst | 9 - doc/source/setup.rst | 33 - doc/source/usage.rst | 322 +------ genchart.py | 273 ------ instance.py | 320 ------- iperf_tool.py | 206 ---- {vmtp => kloudbuster}/__init__.py | 2 +- {scale => kloudbuster}/base_compute.py | 0 {scale => kloudbuster}/base_network.py | 0 {scale => kloudbuster}/cfg.scale.yaml | 0 {scale => kloudbuster}/cfg.tenants.yaml | 0 {scale => kloudbuster}/cfg.topo.yaml | 0 {scale => kloudbuster}/credentials.py | 0 {scale => kloudbuster}/dib/README.rst | 0 {scale => kloudbuster}/dib/Vagrantfile | 0 {scale => kloudbuster}/dib/build-image.sh | 0 .../dib/elements/kloudbuster/README.rst | 0 .../dib/elements/kloudbuster/element-deps | 0 .../kloudbuster/package-installs.yaml | 0 .../kloudbuster/post-install.d/01-kb-script | 0 .../post-install.d/99-cloudcfg-edit | 0 .../kloudbuster/static/etc/nginx/nginx.conf | 0 .../kloudbuster/static/kb_test/kb_vm_agent.py | 0 {scale => kloudbuster}/force_cleanup | 0 {scale => kloudbuster}/kb_config.py | 0 {scale => kloudbuster}/kb_gen_chart.py | 0 {scale => kloudbuster}/kb_runner.py | 0 {scale => kloudbuster}/kb_scheduler.py | 0 {scale => kloudbuster}/kb_server/MANIFEST.in | 0 {scale => kloudbuster}/kb_server/README | 0 {scale => kloudbuster}/kb_server/config.py | 0 .../kb_server/kb_server/__init__.py | 0 .../kb_server/kb_server/app.py | 0 .../kb_server/controllers/__init__.py | 0 .../kb_server/kb_server/controllers/config.py | 0 .../kb_server/kb_server/controllers/root.py | 0 .../kb_server/kb_server/model/__init__.py | 0 {scale => kloudbuster}/kb_server/setup.cfg | 0 {scale => kloudbuster}/kb_server/setup.py | 0 {scale => kloudbuster}/kb_tpl.jinja | 0 {scale => kloudbuster}/kb_vm_agent.py | 0 {scale => kloudbuster}/kloudbuster.py | 6 +- {scale => kloudbuster}/log.py | 0 {scale => kloudbuster}/perf_instance.py | 0 {scale => kloudbuster}/perf_tool.py | 0 {scale => kloudbuster}/tenant.py | 0 {vmtp => kloudbuster}/tests/__init__.py | 0 {vmtp => kloudbuster}/tests/base.py | 0 .../tests/test_kloudbuster.py | 8 +- {scale => kloudbuster}/users.py | 0 {scale => kloudbuster}/wrk_tool.py | 0 monitor.py | 443 --------- network.py | 390 -------- nuttcp_tool.py | 203 ---- openstack-common.conf | 2 +- perf_instance.py | 110 --- perf_tool.py | 293 ------ pns_mongo.py | 142 --- pnsdb_summary.py | 328 ------- pylintrc | 17 - requirements.txt | 8 - run_tests.sh | 2 - scale/README | 8 - scale/sshutils.py | 668 ------------- setup.cfg | 23 +- setup.py | 9 +- sshutils.py | 668 ------------- test-requirements.txt | 7 +- tools/iperf | Bin 68488 -> 0 bytes tools/nuttcp-7.3.2 | Bin 154592 -> 0 bytes tox.ini | 28 +- vmtp.py | 897 ------------------ 93 files changed, 113 insertions(+), 6984 deletions(-) delete mode 100644 .dockerignore create mode 100644 CONTRIBUTING.rst delete mode 100644 Dockerfile delete mode 100644 cfg.default.yaml delete mode 100644 cfg.existing.yaml delete mode 100644 compute.py delete mode 100644 credentials.py delete mode 100644 doc/source/_static/example.json mode change 100644 => 100755 doc/source/conf.py delete mode 100644 doc/source/images/flows.png delete mode 100644 doc/source/images/genchart-sample.png delete mode 100644 doc/source/implementation.rst delete mode 100644 doc/source/issue.rst delete mode 100644 doc/source/setup.rst delete mode 100755 genchart.py delete mode 100644 instance.py delete mode 100644 iperf_tool.py rename {vmtp => kloudbuster}/__init__.py (93%) rename {scale => kloudbuster}/base_compute.py (100%) rename {scale => kloudbuster}/base_network.py (100%) rename {scale => kloudbuster}/cfg.scale.yaml (100%) rename {scale => kloudbuster}/cfg.tenants.yaml (100%) rename {scale => kloudbuster}/cfg.topo.yaml (100%) rename {scale => kloudbuster}/credentials.py (100%) rename {scale => kloudbuster}/dib/README.rst (100%) rename {scale => kloudbuster}/dib/Vagrantfile (100%) rename {scale => kloudbuster}/dib/build-image.sh (100%) rename {scale => kloudbuster}/dib/elements/kloudbuster/README.rst (100%) rename {scale => kloudbuster}/dib/elements/kloudbuster/element-deps (100%) rename {scale => kloudbuster}/dib/elements/kloudbuster/package-installs.yaml (100%) rename {scale => kloudbuster}/dib/elements/kloudbuster/post-install.d/01-kb-script (100%) rename {scale => kloudbuster}/dib/elements/kloudbuster/post-install.d/99-cloudcfg-edit (100%) rename {scale => kloudbuster}/dib/elements/kloudbuster/static/etc/nginx/nginx.conf (100%) rename {scale => kloudbuster}/dib/elements/kloudbuster/static/kb_test/kb_vm_agent.py (100%) rename {scale => kloudbuster}/force_cleanup (100%) rename {scale => kloudbuster}/kb_config.py (100%) rename {scale => kloudbuster}/kb_gen_chart.py (100%) rename {scale => kloudbuster}/kb_runner.py (100%) rename {scale => kloudbuster}/kb_scheduler.py (100%) rename {scale => kloudbuster}/kb_server/MANIFEST.in (100%) rename {scale => kloudbuster}/kb_server/README (100%) rename {scale => kloudbuster}/kb_server/config.py (100%) rename {scale => kloudbuster}/kb_server/kb_server/__init__.py (100%) rename {scale => kloudbuster}/kb_server/kb_server/app.py (100%) rename {scale => kloudbuster}/kb_server/kb_server/controllers/__init__.py (100%) rename {scale => kloudbuster}/kb_server/kb_server/controllers/config.py (100%) rename {scale => kloudbuster}/kb_server/kb_server/controllers/root.py (100%) rename {scale => kloudbuster}/kb_server/kb_server/model/__init__.py (100%) rename {scale => kloudbuster}/kb_server/setup.cfg (100%) rename {scale => kloudbuster}/kb_server/setup.py (100%) rename {scale => kloudbuster}/kb_tpl.jinja (100%) rename {scale => kloudbuster}/kb_vm_agent.py (100%) rename {scale => kloudbuster}/kloudbuster.py (99%) mode change 100644 => 100755 rename {scale => kloudbuster}/log.py (100%) rename {scale => kloudbuster}/perf_instance.py (100%) rename {scale => kloudbuster}/perf_tool.py (100%) rename {scale => kloudbuster}/tenant.py (100%) rename {vmtp => kloudbuster}/tests/__init__.py (100%) rename {vmtp => kloudbuster}/tests/base.py (100%) rename vmtp/tests/test_vmtp.py => kloudbuster/tests/test_kloudbuster.py (84%) rename {scale => kloudbuster}/users.py (100%) rename {scale => kloudbuster}/wrk_tool.py (100%) delete mode 100755 monitor.py delete mode 100755 network.py delete mode 100644 nuttcp_tool.py delete mode 100644 perf_instance.py delete mode 100644 perf_tool.py delete mode 100755 pns_mongo.py delete mode 100755 pnsdb_summary.py delete mode 100644 pylintrc delete mode 100644 run_tests.sh delete mode 100644 scale/README delete mode 100644 scale/sshutils.py mode change 100755 => 100644 setup.py delete mode 100644 sshutils.py delete mode 100755 tools/iperf delete mode 100755 tools/nuttcp-7.3.2 delete mode 100755 vmtp.py diff --git a/.coveragerc b/.coveragerc index 444ba6d..222e3c5 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,7 +1,7 @@ [run] branch = True -source = vmtp -omit = vmtp/tests/*,vmtp/openstack/* +source = kloudbuster +omit = kloudbuster/openstack/* [report] ignore-errors = True diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 1bd415a..0000000 --- a/.dockerignore +++ /dev/null @@ -1,8 +0,0 @@ -ansible -installer -requirements-dev.txt -cloud_init* -.git -.gitignore -.gitreview -.pylintrc diff --git a/.gitignore b/.gitignore index 69f33b8..9bb70d5 100644 --- a/.gitignore +++ b/.gitignore @@ -54,11 +54,8 @@ ChangeLog *cscope* .ropeproject/ -# vmtp -*.local* -*.json - # KloudBuster +*.json *.html *.qcow2 scale/dib/kloudbuster.d/ diff --git a/.mailmap b/.mailmap index 5f2cf35..7c76371 100644 --- a/.mailmap +++ b/.mailmap @@ -2,4 +2,4 @@ # # -vmtp-core@cisco.com +kloudbuster-core@lists.launchpad.net diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000..75c884b --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,17 @@ +If you would like to contribute to the development of OpenStack, you must +follow the steps in this page: + + http://docs.openstack.org/infra/manual/developers.html + +If you already have a good understanding of how the system works and your +OpenStack accounts are set up, you can skip to the development workflow +section of this documentation to learn how changes to OpenStack should be +submitted for review via the Gerrit tool: + + http://docs.openstack.org/infra/manual/developers.html#development-workflow + +Pull requests submitted through GitHub will be ignored. + +Bugs should be filed on Launchpad, not GitHub: + + https://bugs.launchpad.net/kloudbuster diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 9275f1c..0000000 --- a/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -# docker file for creating a container that has vmtp installed and ready to use -FROM ubuntu:14.04 -MAINTAINER vmtp-core - -# Install VMTP script and dependencies -RUN apt-get update && apt-get install -y \ - lib32z1-dev \ - libffi-dev \ - libssl-dev \ - libxml2-dev \ - libxslt1-dev \ - libyaml-dev \ - openssh-client \ - python \ - python-dev \ - python-lxml \ - python-pip \ - && rm -rf /var/lib/apt/lists/* - -COPY . /vmtp/ - -RUN pip install -r /vmtp/requirements.txt - diff --git a/HACKING.rst b/HACKING.rst index 4f2031d..15f84e5 100644 --- a/HACKING.rst +++ b/HACKING.rst @@ -1,4 +1,4 @@ -vmtp Style Commandments +kloudbuster Style Commandments =============================================== Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/ diff --git a/README.rst b/README.rst index 952db7b..bebbbdb 100644 --- a/README.rst +++ b/README.rst @@ -1,126 +1,19 @@ -======== -Overview -======== +=============================== +kloudbuster +=============================== -VMTP is a data path performance measurement tool for OpenStack clouds. +KloudBuster is a open source tool that allows anybody to load any Neutron OpenStack cloud at massive data plane scale swiftly and effortlessly. +Please feel here a long description which must be at least 3 lines wrapped on +80 cols, so that distribution package maintainers can use it in their packages. +Note that this is a hard requirement. + +* Free software: Apache license +* Documentation: http://docs.openstack.org/developer/kloudbuster +* Source: http://git.openstack.org/cgit/openstack/kloudbuster +* Bugs: http://bugs.launchpad.net/kloudbuster Features -------- -Have you ever had the need for a quick, simple and automatable way to get VM-level or host-level single-flow throughput and latency numbers from any OpenStack cloud, and take into account various Neutron topologies? Or check whether some OpenStack configuration option, Neutron plug-in performs to expectation or if there is any data path impact for upgrading to a different OpenStack release? - -VMTP is a small python application that will automatically perform ping connectivity, round trip time measurement (latency) and TCP/UDP throughput measurement for the following East/West flows on any OpenStack deployment: - -* VM to VM same network (private fixed IP, flow #1) -* VM to VM different network using fixed IP (same as intra-tenant L3 fixed IP, flow #2) -* VM to VM different network using floating IP and NAT (same as floating IP inter-tenant L3, flow #3) - -Optionally, when an external Linux host is available for testing North/South flows: - -* External host/VM download and upload throughput/latency (L3/floating IP, flow #4 and #5) - -.. image:: images/flows.png - -Optionally, when SSH login to any Linux host (native or virtual) is available: - -* Host to host process-level throughput/latency (intra-node and inter-node) - -Optionally, VMTP can extract automatically CPU usage from all native hosts in the cloud during the throughput tests, provided the Ganglia monitoring service (gmond) is installed and enabled on those hosts. - -For VM-related flows, VMTP will automatically create the necessary OpenStack resources (router, networks, subnets, key pairs, security groups, test VMs) using the public OpenStack API, install the test tools then orchestrate them to gather the throughput measurements then cleanup all related resources before exiting. - -See the usage page for the description of all the command line arguments supported by VMTP. - - -Pre-requisite -------------- - -VMTP runs on any Python 2.X envirnment (validated on Linux and MacOSX). - -For VM related performance measurements -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -* Access to the cloud Horizon Dashboard (to retrieve the openrc file) -* 1 working external network pre-configured on the cloud (VMTP will pick the first one found) -* At least 2 floating IP if an external router is configured or 3 floating IP if there is no external router configured -* 1 Linux image available in OpenStack (any distribution) -* A configuration file that is properly set for the cloud to test (see "Configuration File" section below) - -For native/external host throughputs -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -* A public key must be installed on the target hosts (see ssh password-less access below) - -For pre-existing native host throughputs -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -* Firewalls must be configured to allow TCP/UDP ports 5001 and TCP port 5002 - -For running VMTP Docker Image -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -* Docker is installed. See `here `_ for instructions. - -Sample Results Output ---------------------- - -VMTP will display the results to stdout with the following data: - -.. code:: - - - Session general information (date, auth_url, OpenStack encaps, VMTP version, OpenStack release, Agent type, CPU...) - - List of results per flow, for each flow: - | flow name - | to and from IP addresses - | to and from availability zones (if VM) - | - results: - | | -TCP - | | | packet size - | | | throughput value - | | | number of retransmissions - | | | round trip time in ms - | | | - CPU usage (if enabled), for each host in the openstack cluster - | | | | baseline (before test starts) - | | | | 1 or more readings during test - | | -UDP - | | | - for each packet size - | | | | throughput value - | | | | loss rate - | | | | CPU usage (if enabled) - | | - ICMP - | | | average, min, max and stddev round trip time in ms - -Detailed results can also be stored in a file in JSON format using the *--json* command line argument and/or stored directly into a MongoDB server. See :download:`here <_static/example.json>` for an example JSON file that is generated by VMTP. -The packaged python tool genchart.py can be used to generate from the JSON result files column charts in HTML format visible from any browser. - -Example of column chart generated by genchart.py: - -.. image:: images/genchart-sample.png - -Limitations and Caveats ------------------------ - -VMTP only measures performance for single-flows at the socket/TCP/UDP level (in a VM or natively). Measured numbers therefore reflect what most applications will see. - -It is not designed to measure driver level data path performance from inside a VM (such as bypassing the kernel TCP stack and write directly to virtio), there are better tools that can address this type of mesurement. - - -Licensing ---------- - -VMTP is licensed under Apache License 2.0 and comes packaged with the following tools for convenience: - -* iperf: BSD License (https://iperf.fr/license.html, source code: https://iperf.fr) -* nuttcp: GPL v2 License (http://nuttcp.net/nuttcp/beta/LICENSE, source code: http://nuttcp.net/nuttcp/beta/nuttcp-7.3.2.c) - -Redistribution of nuttcp and iperf is governed by their respective licenses. Please make sure you read and understand each one before further redistributing VMTP downstream. - -Links ------ - -* Documentation: http://vmtp.readthedocs.org/en/latest -* Source: http://git.openstack.org/cgit/stackforge/vmtp -* Supports/Bugs: https://launchpad.net/vmtp -* Mailing List: vmtp-core@lists.launchpad.net - +* TODO diff --git a/cfg.default.yaml b/cfg.default.yaml deleted file mode 100644 index a5e1c37..0000000 --- a/cfg.default.yaml +++ /dev/null @@ -1,185 +0,0 @@ -# -# VMTP default configuration file -# -# This configuration file is ALWAYS loaded by VMTP and should never be modified by users. -# To specify your own property values, always define them in a separate config file -# and pass that file to the script using -c or --config -# Property values in that config file will override the default values in the current file -# ---- - -# Name of the image to use for launching the test VMs. This name must be -# the exact same name used in OpenStack (as shown from 'nova image-list') -# Any image running Linux should work (Fedora, Ubuntu, CentOS...) -image_name: 'Ubuntu Server 14.04' -#image_name: 'Fedora 21' - -# User name to use to ssh to the test VMs -# This is specific to the image being used -ssh_vm_username: 'ubuntu' -#ssh_vm_username: fedora - -# Name of the flavor to use for the test VMs -# This name must be an exact match to a flavor name known by the target -# OpenStack deployment (as shown from 'nova flavor-list') -flavor_type: 'm1.small' - -# Name of the availability zone to use for the test VMs -# Must be one of the zones listed by 'nova availability-zone-list' -# If the zone selected contains more than 1 compute node, the script -# will determine inter-node and intra-node throughput. If it contains only -# 1 compute node, only intra-node troughput will be measured. -# If empty (default), VMTP will automatically pick the first 2 hosts -# that are compute nodes regardless of the availability zone -#availability_zone: 'nova' -availability_zone: - -# DNS server IP addresses to use for the VM (list of 1 or more DNS servers) -# This default DNS server is available on the Internet, -# Change this to use a different DNS server if necessary, -dns_nameservers: [ '8.8.8.8' ] - -# VMTP can automatically download a VM image if the image named by -# image_name is missing, for that you need to specify a URL where -# the image can be retrieved -# -# A link to a Ubuntu Server 14.04 qcow2 image can be used here: -# https://cloud-images.ubuntu.com/trusty/current/trusty-server-cloudimg-amd64-disk1.img -vm_image_url: '' - -# ----------------------------------------------------------------------------- -# These variables are not likely to be changed - -# Set this variable to a network name if you want the script to reuse -# a specific existing external network. If empty, the script will reuse the -# first external network it can find (the cloud must have at least 1 -# external network defined and available for use) -# When set, ignore floating ip creation and reuse existing management network for tests -reuse_network_name : - -# Use of the script for special deployments -floating_ip: True - -# Set this to an existing VM name if the script should not create new VM -# and reuse existing VM -reuse_existing_vm : - -# Set config drive to true to bypass metadata service and use config drive -# 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 -router_name: 'pns-router' - -# Defaul names for the internal networks used by the -# script. If an existing network with this name exists it will be reused. -# Otherwise a new internal network will be created with that name. -# 2 networks are needed to test the case of network to network communication -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 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: - -# 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 -vm_name_server: 'TestServer' - -# name of the client VM -vm_name_client: 'TestClient' - -# name of the security group to create and use -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 -ping_count: 2 -ping_pass_threshold: 80 - -# Max retry count for ssh to a VM (5 seconds between retries) -ssh_retry_count: 50 - -# General retry count -generic_retry_count: 50 - -# Times to run when measuring TCP Throughput -tcp_tp_loop_count: 3 - -# TCP throughput list of packet sizes to measure -# Can be overridden at the command line using --tcpbuf -tcp_pkt_sizes: [65536] - -# UDP throughput list of packet sizes to measure -# By default we measure for small, medium and large packets -# Can be overridden at the command line using --udpbuf -udp_pkt_sizes: [128, 1024, 8192] - -# UDP packet loss rate threshold in percentage beyond which bandwidth -# iterations stop and below which iteration with a higher -# bandwidth continues -# The first number is the minimal loss rate (inclusive) -# The second number is the maximum loss rate (inclusive) -# Iteration to find the "optimal" bandwidth will stop as soon as the loss rate -# falls within that range: min <= loss_rate <= max -# The final throughput measurement may return a loss rate out of this range -# as that measurement is taken on a longer time than when iterating to find -# the optimal throughput -# -udp_loss_rate_range: [2, 5] - -# The default bandwidth limit (in Kbps) for TCP/UDP flow measurement -# 0 means unlimited, which can be overridden at the command line using --bandwidth -vm_bandwidth: 0 - -####################################### -# VMTP MongoDB Connection information -####################################### - -######################################## -# Default MongoDB port is 27017, to override -#vmtp_mongod_port: - -######################################## -# MongoDB pns database. -# use "official_db" for offical runs only. -######################################## -vmtp_db: "client_db" - -######################################## -# MongoDB collection name. -######################################## -vmtp_collection: "pns_web_entry" - diff --git a/cfg.existing.yaml b/cfg.existing.yaml deleted file mode 100644 index 584f908..0000000 --- a/cfg.existing.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# -# Example of configuration where we froce the use of a specific external network and -# use provider network (no floating IP) -reuse_network_name : 'prov1' - -# Floating ip false is a provider network where we simply attach to it -floating_ip : False - -# Floating ip is true by default: -# attach to existing network, create a floating ip and attach instance to it diff --git a/compute.py b/compute.py deleted file mode 100644 index dcbfebc..0000000 --- a/compute.py +++ /dev/null @@ -1,463 +0,0 @@ -# Copyright 2014 Cisco Systems, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -'''Module for Openstack compute operations''' - -import os -import subprocess -import time - -import novaclient -import novaclient.exceptions as exceptions - -class Compute(object): - - def __init__(self, nova_client, config): - self.novaclient = nova_client - self.config = config - - def find_image(self, image_name): - try: - image = self.novaclient.images.find(name=image_name) - return image - except novaclient.exceptions.NotFound: - return None - - def upload_image_via_url(self, glance_client, final_image_name, image_url, retry_count=60): - ''' - Directly uploads image to Nova via URL if image is not present - ''' - - # Here is the deal: - # Idealy, we should better to use the python library glanceclient to perform the - # image uploades. However, due to a limitation of the v2.0 API right now, it is - # impossible to tell Glance to download the image from a URL directly. - # - # There are two steps to create the image: - # (1) Store the binary image data into Glance; - # (2) Store the metadata about the image into Glance; - # PS: The order does not matter. - # - # The REST API allows to do two steps in one if a Location header is provided with - # the POST request. (REF: http://developer.openstack.org/api-ref-image-v2.html) - # - # However the python API doesn't support a customized header in POST request. - # So we have to do two steps in two calls. - # - # The good thing is: the API does support (2) perfectly, but for (1) it is only - # accepting the data from local, not remote URL. So... Ur... Let's keep the CLI - # version as the workaround for now. - - # # upload in glance - # image = glance_client.images.create( - # name=str(final_image_name), disk_format="qcow2", container_format="bare", - # Location=image_url) - # glance_client.images.add_location(image.id, image_url, image) - - # sys.exit(0) - # for retry_attempt in range(retry_count): - # if image.status == "active": - # print 'Image: %s successfully uploaded to Nova' % (final_image_name) - # return 1 - # # Sleep between retries - # if self.config.debug: - # print "Image is not yet active, retrying %s of %s... [%s]" \ - # % ((retry_attempt + 1), retry_count, image.status) - # time.sleep(5) - - # upload in glance - glance_cmd = "glance image-create --name=\"" + str(final_image_name) + \ - "\" --disk-format=qcow2" + " --container-format=bare " + \ - " --is-public True --copy-from " + image_url - if self.config.debug: - print "Will update image to glance via CLI: %s" % (glance_cmd) - subprocess.check_output(glance_cmd, shell=True) - - # check for the image in glance - glance_check_cmd = "glance image-list --name \"" + str(final_image_name) + "\"" - for retry_attempt in range(retry_count): - result = subprocess.check_output(glance_check_cmd, shell=True) - if "active" in result: - print 'Image: %s successfully uploaded to Nova' % (final_image_name) - return 1 - # Sleep between retries - if self.config.debug: - print "Image not yet active, retrying %s of %s..." \ - % ((retry_attempt + 1), retry_count) - time.sleep(2) - - print 'ERROR: Cannot upload image %s from URL: %s' % (final_image_name, image_url) - return 0 - - # Remove keypair name from openstack if exists - def remove_public_key(self, name): - keypair_list = self.novaclient.keypairs.list() - for key in keypair_list: - if key.name == name: - self.novaclient.keypairs.delete(name) - print 'Removed public key %s' % (name) - break - - # Test if keypair file is present if not create it - 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 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 - def add_public_key(self, name, public_key_file): - self.remove_public_key(name) - # extract the public key from the file - public_key = None - try: - with open(os.path.expanduser(public_key_file)) as pkf: - public_key = pkf.read() - except IOError as exc: - print 'ERROR: Cannot open public key file %s: %s' % \ - (public_key_file, exc) - return None - 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 - - # Create a server instance with name vmname - # and check that it gets into the ACTIVE state - def create_server(self, vmname, image, flavor, key_name, - nic, sec_group, avail_zone=None, user_data=None, - config_drive=None, - retry_count=10): - - if sec_group: - security_groups = [sec_group.id] - else: - security_groups = None - # Also attach the created security group for the test - instance = self.novaclient.servers.create(name=vmname, - image=image, - flavor=flavor, - key_name=key_name, - nics=nic, - availability_zone=avail_zone, - userdata=user_data, - config_drive=config_drive, - security_groups=security_groups) - if not instance: - 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): - servers_list = self.novaclient.servers.list() - return servers_list - - def find_floating_ips(self): - floating_ip = self.novaclient.floating_ips.list() - return floating_ip - - # Return the server network for a server - def find_server_network(self, vmname): - servers_list = self.get_server_list() - for server in servers_list: - if server.name == vmname and server.status == "ACTIVE": - return server.networks - return None - - # Returns True if server is present false if not. - # Retry for a few seconds since after VM creation sometimes - # it takes a while to show up - def find_server(self, vmname, retry_count): - for retry_attempt in range(retry_count): - servers_list = self.get_server_list() - for server in servers_list: - if server.name == vmname and server.status == "ACTIVE": - return True - # Sleep between retries - if self.config.debug: - print "[%s] VM not yet found, retrying %s of %s..." \ - % (vmname, (retry_attempt + 1), retry_count) - time.sleep(2) - print "[%s] VM not found, after %s attempts" % (vmname, retry_count) - return False - - # Returns True if server is found and deleted/False if not, - # retry the delete if there is a delay - def delete_server_by_name(self, vmname): - servers_list = self.get_server_list() - for server in servers_list: - if server.name == vmname: - print 'deleting server %s' % (server) - self.novaclient.servers.delete(server) - return True - return False - - def delete_server(self, server): - self.novaclient.servers.delete(server) - - def find_flavor(self, flavor_type): - flavor = self.novaclient.flavors.find(name=flavor_type) - return flavor - - def normalize_az_host(self, az, host): - if not az: - az = self.config.availability_zone - return az + ':' + host - - def auto_fill_az(self, host_list, host): - ''' - no az provided, if there is a host list we can auto-fill the az - else we use the configured az if available - else we return an error - ''' - if host_list: - for hyp in host_list: - if hyp.host_name == host: - return self.normalize_az_host(hyp.zone, host) - # no match on host - print('Error: passed host name does not exist: ' + host) - return None - if self.config.availability_zone: - return self.normalize_az_host(None, host) - print('Error: --hypervisor passed without an az and no az configured') - return None - - def sanitize_az_host(self, host_list, az_host): - ''' - host_list: list of hosts as retrieved from openstack (can be empty) - az_host: either a host or a az:host string - if a host, will check host is in the list, find the corresponding az and - return az:host - if az:host is passed will check the host is in the list and az matches - if host_list is empty, will return the configured az if there is no - az passed - ''' - if ':' in az_host: - # no host_list, return as is (no check) - if not host_list: - return az_host - # if there is a host_list, extract and verify the az and host - az_host_list = az_host.split(':') - zone = az_host_list[0] - host = az_host_list[1] - for hyp in host_list: - if hyp.host_name == host: - if hyp.zone == zone: - # matches - return az_host - # else continue - another zone with same host name? - # no match - print('Error: no match for availability zone and host ' + az_host) - return None - else: - return self.auto_fill_az(host_list, az_host) - - # - # Return a list of 0, 1 or 2 az:host - # - # The list is computed as follows: - # The list of all hosts is retrieved first from openstack - # if this fails, checks and az auto-fill are disabled - # - # If the user provides a list of hypervisors (--hypervisor) - # that list is checked and returned - # - # If the user provides a configured az name (config.availability_zone) - # up to the first 2 hosts from the list that match the az are returned - # - # If the user did not configure an az name - # up to the first 2 hosts from the list are returned - # Possible return values: - # [ az ] - # [ az:hyp ] - # [ az1:hyp1, az2:hyp2 ] - # [] if an error occurred (error message printed to console) - # - def get_az_host_list(self): - avail_list = [] - host_list = [] - - try: - host_list = self.novaclient.services.list() - except novaclient.exceptions.Forbidden: - print ('Warning: Operation Forbidden: could not retrieve list of hosts' - ' (likely no permission)') - - # the user has specified a list of 1 or 2 hypervisors to use - if self.config.hypervisors: - for hyp in self.config.hypervisors: - hyp = self.sanitize_az_host(host_list, hyp) - if hyp: - avail_list.append(hyp) - else: - return [] - # if the user did not specify an az, insert the configured az - if ':' not in hyp: - if self.config.availability_zone: - hyp = self.normalize_az_host(None, hyp) - else: - return [] - # pick first 2 matches at most - if len(avail_list) == 2: - break - print 'Using hypervisors:' + ', '.join(avail_list) - else: - for host in host_list: - # this host must be a compute node - if host.binary != 'nova-compute' or host.state != 'up': - continue - candidate = None - if self.config.availability_zone: - if host.zone == self.config.availability_zone: - candidate = self.normalize_az_host(None, host.host) - else: - candidate = self.normalize_az_host(host.zone, host.host) - if candidate: - avail_list.append(candidate) - # pick first 2 matches at most - if len(avail_list) == 2: - break - - # if empty we insert the configured az - if not avail_list: - - if not self.config.availability_zone: - print('Error: availability_zone must be configured') - elif host_list: - print('Error: no host matching the selection for availability zone: ' - + self.config.availability_zone) - avail_list = [] - else: - avail_list = [self.config.availability_zone] - return avail_list - - # Given 2 VMs test if they are running on same Host or not - def check_vm_placement(self, vm_instance1, vm_instance2): - try: - server_instance_1 = self.novaclient.servers.get(vm_instance1) - server_instance_2 = self.novaclient.servers.get(vm_instance2) - if server_instance_1.hostId == server_instance_2.hostId: - return True - else: - return False - except novaclient.exceptions: - print "Exception in retrieving the hostId of servers" - - # Create a new security group with appropriate rules - def security_group_create(self): - # check first the security group exists - # May throw exceptions.NoUniqueMatch or NotFound - try: - group = self.novaclient.security_groups.find(name=self.config.security_group_name) - return group - except exceptions.NotFound: - group = self.novaclient.security_groups.create(name=self.config.security_group_name, - description="PNS Security group") - # Once security group try to find it iteratively - # (this check may no longer be necessary) - for _ in range(self.config.generic_retry_count): - group = self.novaclient.security_groups.get(group) - if group: - self.security_group_add_rules(group) - return group - else: - time.sleep(1) - return None - # except exceptions.NoUniqueMatch as exc: - # raise exc - - # Delete a security group - def security_group_delete(self, group): - if group: - print "Deleting security group" - self.novaclient.security_groups.delete(group) - - # Add rules to the security group - def security_group_add_rules(self, group): - # Allow ping traffic - self.novaclient.security_group_rules.create(group.id, - 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", - from_port=22, - to_port=22) - # Allow TCP/UDP traffic for perf tools like iperf/nuttcp - # 5001: Data traffic (standard iperf data port) - # 5002: Control traffic (non standard) - # note that 5000/tcp is already picked by openstack keystone - 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/credentials.py b/credentials.py deleted file mode 100644 index ddb62b2..0000000 --- a/credentials.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright 2014 Cisco Systems, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# - -# Module for credentials in Openstack -import getpass -import os -import re - - -class Credentials(object): - - def get_credentials(self): - dct = {} - dct['username'] = self.rc_username - dct['password'] = self.rc_password - dct['auth_url'] = self.rc_auth_url - dct['tenant_name'] = self.rc_tenant_name - return dct - - def get_nova_credentials(self): - dct = {} - dct['username'] = self.rc_username - dct['api_key'] = self.rc_password - dct['auth_url'] = self.rc_auth_url - dct['project_id'] = self.rc_tenant_name - return dct - - def get_nova_credentials_v2(self): - dct = self.get_nova_credentials() - dct['version'] = 2 - return dct - - # - # Read a openrc file and take care of the password - # The 2 args are passed from the command line and can be None - # - def __init__(self, openrc_file, pwd, no_env): - self.rc_password = None - self.rc_username = None - self.rc_tenant_name = None - self.rc_auth_url = None - success = True - - if openrc_file: - if os.path.exists(openrc_file): - export_re = re.compile('export OS_([A-Z_]*)="?(.*)') - for line in open(openrc_file): - line = line.strip() - mstr = export_re.match(line) - if mstr: - # get rif of posible trailing double quote - # the first one was removed by the re - name = mstr.group(1) - value = mstr.group(2) - if value.endswith('"'): - value = value[:-1] - # get rid of password assignment - # echo "Please enter your OpenStack Password: " - # read -sr OS_PASSWORD_INPUT - # export OS_PASSWORD=$OS_PASSWORD_INPUT - if value.startswith('$'): - continue - # now match against wanted variable names - if name == 'USERNAME': - self.rc_username = value - elif name == 'AUTH_URL': - self.rc_auth_url = value - elif name == 'TENANT_NAME': - self.rc_tenant_name = value - else: - print 'Error: rc file does not exist %s' % (openrc_file) - success = False - elif not no_env: - # no openrc file passed - we assume the variables have been - # sourced by the calling shell - # 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) - success = False - if success: - self.rc_username = os.environ['OS_USERNAME'] - self.rc_auth_url = os.environ['OS_AUTH_URL'] - self.rc_tenant_name = os.environ['OS_TENANT_NAME'] - - # always override with CLI argument if provided - if pwd: - self.rc_password = pwd - # if password not know, check from env variable - elif self.rc_auth_url and not self.rc_password and success: - if 'OS_PASSWORD' in os.environ and not no_env: - self.rc_password = os.environ['OS_PASSWORD'] - else: - # interactively ask for password - self.rc_password = getpass.getpass( - 'Please enter your OpenStack Password: ') - if not self.rc_password: - self.rc_password = "" diff --git a/doc/Makefile b/doc/Makefile index a3cd27f..d8ce958 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -85,17 +85,17 @@ qthelp: @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/vmtp.qhcp" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/kloudbuster.qhcp" @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/vmtp.qhc" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/kloudbuster.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/vmtp" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/vmtp" + @echo "# mkdir -p $$HOME/.local/share/devhelp/kloudbuster" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/kloudbuster" @echo "# devhelp" epub: diff --git a/doc/source/_static/example.json b/doc/source/_static/example.json deleted file mode 100644 index 08dda48..0000000 --- a/doc/source/_static/example.json +++ /dev/null @@ -1,409 +0,0 @@ -{ - "args": "vmtp.py -c cfg.default.yaml -r ../admin-openrc.sh -p --json juno_ovs_vxlan_2.json --mongod_server 172.29.87.29 --controller-node -d --test_description Yichen's testbed", - "auth_url": "http://172.29.87.180:5000/v2.0", - "cpu_info": "40 * Intel(R) Xeon(R) CPU E5-2660 v2 @ 2.20GHz", - "date": "2015-03-04 22:33:40", - "distro": "CentOS Linux 7", - "encapsulation": "vxlan", - "flows": [ - { - "az_from": "nova:hh23-6", - "az_to": "nova:hh23-6", - "desc": "VM to VM same network fixed IP (intra-node)", - "distro_id": "Ubuntu", - "distro_version": "14.04", - "ip_from": "192.168.1.4", - "ip_to": "192.168.1.2", - "results": [ - { - "pkt_size": 65536, - "protocol": "TCP", - "rtt_ms": 0.28, - "throughput_kbps": 14318464, - "tool": "nuttcp-7.3.2" - }, - { - "pkt_size": 65536, - "protocol": "TCP", - "rtt_ms": 0.12, - "throughput_kbps": 14426352, - "tool": "nuttcp-7.3.2" - }, - { - "pkt_size": 65536, - "protocol": "TCP", - "rtt_ms": 0.13, - "throughput_kbps": 14247563, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 0.11, - "pkt_size": 128, - "protocol": "UDP", - "throughput_kbps": 127744, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 0.12, - "pkt_size": 1024, - "protocol": "UDP", - "throughput_kbps": 1021703, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 0.17, - "pkt_size": 8192, - "protocol": "UDP", - "throughput_kbps": 2496542, - "tool": "nuttcp-7.3.2" - }, - { - "protocol": "ICMP", - "rtt_avg_ms": "0.321", - "rtt_max_ms": "0.741", - "rtt_min_ms": "0.187", - "rtt_stddev": "0.212", - "rx_packets": "5", - "tool": "ping", - "tx_packets": "5" - } - ] - }, - { - "az_from": "nova:hh23-6", - "az_to": "nova:hh23-6", - "desc": "VM to VM different network fixed IP (intra-node)", - "distro_id": "Ubuntu", - "distro_version": "14.04", - "ip_from": "192.168.2.2", - "ip_to": "192.168.1.2", - "results": [ - { - "pkt_size": 65536, - "protocol": "TCP", - "retrans": 116, - "rtt_ms": 0.67, - "throughput_kbps": 1292957, - "tool": "nuttcp-7.3.2" - }, - { - "pkt_size": 65536, - "protocol": "TCP", - "retrans": 218, - "rtt_ms": 0.58, - "throughput_kbps": 1602299, - "tool": "nuttcp-7.3.2" - }, - { - "pkt_size": 65536, - "protocol": "TCP", - "retrans": 606, - "rtt_ms": 0.59, - "throughput_kbps": 1583186, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 0.94, - "pkt_size": 128, - "protocol": "UDP", - "throughput_kbps": 152745, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 0.39, - "pkt_size": 1024, - "protocol": "UDP", - "throughput_kbps": 1222784, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 2.52, - "pkt_size": 8192, - "protocol": "UDP", - "throughput_kbps": 1342442, - "tool": "nuttcp-7.3.2" - }, - { - "protocol": "ICMP", - "rtt_avg_ms": "0.771", - "rtt_max_ms": "1.126", - "rtt_min_ms": "0.677", - "rtt_stddev": "0.180", - "rx_packets": "5", - "tool": "ping", - "tx_packets": "5" - } - ] - }, - { - "az_from": "nova:hh23-6", - "az_to": "nova:hh23-6", - "desc": "VM to VM different network floating IP (intra-node)", - "distro_id": "Ubuntu", - "distro_version": "14.04", - "ip_from": "192.168.2.2", - "ip_to": "172.29.87.183", - "results": [ - { - "pkt_size": 65536, - "protocol": "TCP", - "retrans": 560, - "rtt_ms": 0.69, - "throughput_kbps": 1407148, - "tool": "nuttcp-7.3.2" - }, - { - "pkt_size": 65536, - "protocol": "TCP", - "retrans": 184, - "rtt_ms": 0.62, - "throughput_kbps": 1475068, - "tool": "nuttcp-7.3.2" - }, - { - "pkt_size": 65536, - "protocol": "TCP", - "retrans": 310, - "rtt_ms": 0.59, - "throughput_kbps": 1529674, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 3.62, - "pkt_size": 128, - "protocol": "UDP", - "throughput_kbps": 153493, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 4.14, - "pkt_size": 1024, - "protocol": "UDP", - "throughput_kbps": 1241424, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 4.37, - "pkt_size": 8192, - "protocol": "UDP", - "throughput_kbps": 1311624, - "tool": "nuttcp-7.3.2" - }, - { - "protocol": "ICMP", - "rtt_avg_ms": "0.646", - "rtt_max_ms": "0.693", - "rtt_min_ms": "0.613", - "rtt_stddev": "0.043", - "rx_packets": "5", - "tool": "ping", - "tx_packets": "5" - } - ] - }, - { - "az_from": "nova:hh23-5", - "az_to": "nova:hh23-6", - "desc": "VM to VM same network fixed IP (inter-node)", - "distro_id": "Ubuntu", - "distro_version": "14.04", - "ip_from": "192.168.1.5", - "ip_to": "192.168.1.2", - "results": [ - { - "pkt_size": 65536, - "protocol": "TCP", - "retrans": 99, - "rtt_ms": 0.34, - "throughput_kbps": 2340466, - "tool": "nuttcp-7.3.2" - }, - { - "pkt_size": 65536, - "protocol": "TCP", - "retrans": 67, - "rtt_ms": 0.43, - "throughput_kbps": 2313315, - "tool": "nuttcp-7.3.2" - }, - { - "pkt_size": 65536, - "protocol": "TCP", - "retrans": 63, - "rtt_ms": 0.32, - "throughput_kbps": 2020005, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 50.66, - "pkt_size": 128, - "protocol": "UDP", - "throughput_kbps": 76095, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 24.04, - "pkt_size": 1024, - "protocol": "UDP", - "throughput_kbps": 920877, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 28.84, - "pkt_size": 8192, - "protocol": "UDP", - "throughput_kbps": 1901142, - "tool": "nuttcp-7.3.2" - }, - { - "protocol": "ICMP", - "rtt_avg_ms": "0.657", - "rtt_max_ms": "1.555", - "rtt_min_ms": "0.331", - "rtt_stddev": "0.453", - "rx_packets": "5", - "tool": "ping", - "tx_packets": "5" - } - ] - }, - { - "az_from": "nova:hh23-5", - "az_to": "nova:hh23-6", - "desc": "VM to VM different network fixed IP (inter-node)", - "distro_id": "Ubuntu", - "distro_version": "14.04", - "ip_from": "192.168.2.4", - "ip_to": "192.168.1.2", - "results": [ - { - "pkt_size": 65536, - "protocol": "TCP", - "retrans": 121, - "rtt_ms": 0.68, - "throughput_kbps": 1344370, - "tool": "nuttcp-7.3.2" - }, - { - "pkt_size": 65536, - "protocol": "TCP", - "retrans": 224, - "rtt_ms": 0.61, - "throughput_kbps": 1448398, - "tool": "nuttcp-7.3.2" - }, - { - "pkt_size": 65536, - "protocol": "TCP", - "retrans": 75, - "rtt_ms": 0.5, - "throughput_kbps": 1301634, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 1.04, - "pkt_size": 128, - "protocol": "UDP", - "throughput_kbps": 161581, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 0.98, - "pkt_size": 1024, - "protocol": "UDP", - "throughput_kbps": 1207335, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 3.82, - "pkt_size": 8192, - "protocol": "UDP", - "throughput_kbps": 1330237, - "tool": "nuttcp-7.3.2" - }, - { - "protocol": "ICMP", - "rtt_avg_ms": "0.648", - "rtt_max_ms": "0.984", - "rtt_min_ms": "0.489", - "rtt_stddev": "0.175", - "rx_packets": "5", - "tool": "ping", - "tx_packets": "5" - } - ] - }, - { - "az_from": "nova:hh23-5", - "az_to": "nova:hh23-6", - "desc": "VM to VM different network floating IP (inter-node)", - "distro_id": "Ubuntu", - "distro_version": "14.04", - "ip_from": "192.168.2.4", - "ip_to": "172.29.87.183", - "results": [ - { - "pkt_size": 65536, - "protocol": "TCP", - "retrans": 201, - "rtt_ms": 0.65, - "throughput_kbps": 1371518, - "tool": "nuttcp-7.3.2" - }, - { - "pkt_size": 65536, - "protocol": "TCP", - "retrans": 133, - "rtt_ms": 0.57, - "throughput_kbps": 1388169, - "tool": "nuttcp-7.3.2" - }, - { - "pkt_size": 65536, - "protocol": "TCP", - "retrans": 68, - "rtt_ms": 0.56, - "throughput_kbps": 1250003, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 2.66, - "pkt_size": 128, - "protocol": "UDP", - "throughput_kbps": 148525, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 2.02, - "pkt_size": 1024, - "protocol": "UDP", - "throughput_kbps": 1174606, - "tool": "nuttcp-7.3.2" - }, - { - "loss_rate": 1.12, - "pkt_size": 8192, - "protocol": "UDP", - "throughput_kbps": 1310265, - "tool": "nuttcp-7.3.2" - }, - { - "protocol": "ICMP", - "rtt_avg_ms": "0.606", - "rtt_max_ms": "0.698", - "rtt_min_ms": "0.462", - "rtt_stddev": "0.086", - "rx_packets": "5", - "tool": "ping", - "tx_packets": "5" - } - ] - } - ], - "l2agent_type": "Open vSwitch agent", - "l2agent_version": "OVS 2.3.1", - "nic_name": "Cisco Systems Inc VIC Ethernet NIC (rev a2)", - "openstack_version": "Juno (2014.2.1)", - "test_description": "Yichen's testbed", - "version": "2.0.1" -} diff --git a/doc/source/conf.py b/doc/source/conf.py old mode 100644 new mode 100755 index 02ab26d..3c1ccd0 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# vmtp documentation build configuration file, created by +# kloudbuster documentation build configuration file, created by # sphinx-quickstart on Fri Feb 13 14:43:59 2015. # # This file is execfile()d with the current directory set to its @@ -14,9 +14,10 @@ import datetime import os -import re import sys +from pbr import version as pbr_ver + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -50,7 +51,7 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'vmtp' +project = u'kloudbuster' copyright = u"%d, OpenStack Foundation" % datetime.datetime.now().year # The version info for the project you're documenting, acts as replacement for @@ -58,11 +59,9 @@ copyright = u"%d, OpenStack Foundation" % datetime.datetime.now().year # built documents. # # The short X.Y version. -vmtp_file = open("../../vmtp.py") -raw_text = vmtp_file.read() -version = re.search(r"__version__\s=\s'(\d+\.\d+\.\d+)'", raw_text).group(1) +version = pbr_ver.VersionInfo(project).version_string() # The full version, including alpha/beta/rc tags. -release = version +release = pbr_ver.VersionInfo(project).version_string_with_vcs() # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -185,7 +184,7 @@ html_static_path = ['_static'] #html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'vmtpdoc' +htmlhelp_basename = 'kloudbusterdoc' # -- Options for LaTeX output --------------------------------------------- @@ -205,7 +204,7 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'vmtp.tex', u'vmtp Documentation', + ('index', 'kloudbuster.tex', u'KloudBuster Documentation', u'OpenStack Foundation', 'manual'), ] @@ -235,7 +234,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'vmtp', u'vmtp Documentation', + ('index', 'kloudbuster', u'KloudBuster Documentation', [u'OpenStack Foundation'], 1) ] @@ -249,8 +248,8 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'vmtp', u'vmtp Documentation', - u'OpenStack Foundation', 'vmtp', 'One line description of project.', + ('index', 'kloudbuster', u'KloudBuster Documentation', + u'OpenStack Foundation', 'kloudbuster', 'One line description of project.', 'Miscellaneous'), ] diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst index c8c0763..1728a61 100644 --- a/doc/source/contributing.rst +++ b/doc/source/contributing.rst @@ -1,115 +1,4 @@ ============ Contributing ============ - -Contribute to VMTP ------------------- - -Below are a simplified version of the workflow to work on VMTP. For complete instructions, you have to follow the Developer's Guide in OpenStack official documents. Refer to :ref:`below section ` for links. - - -Start working -^^^^^^^^^^^^^ - -Before starting, a GitHub/StackForge respository based installation must be done. Refer :ref:`here ` for detailed documentation. - -1. From the root of your workspace, check out a new branch to work on:: - - $ git checkout -b - -2. Happy working on your code for features or bugfixes; - - -Before Commit -^^^^^^^^^^^^^ - -There are some criteria that are enforced to commit to VMTP. Below commands will perform the check and make sure your code complys with it. - -3. PEP 8:: - - $ tox -epep8 - -**Note:** The first run usually takes longer, as tox will create a new virtual environment and download all dependencies. Once that is the done, further run will be very fast. - -4. Run the test suite:: - - $ tox -epython27 - -5. If you made a documentation change (i.e. changes to .rst files), make sure the documentation is built as you expected:: - - $ cd /doc - $ make html - -Once finished, the documentation in HTML format will be ready at /doc/build/html. - - -Submit Review -^^^^^^^^^^^^^ - -6. Commit the code:: - - $ git commit -a - -**Note:** For a feature commit, please supply a clear commit message indicating what the feature is; for a bugfix commit, please also containing a launchpad link to the bug you are working on. - -7. Submit the review:: - - $ git review - -The members in the VMTP team will get notified once the Jenkin verification is passed. So watch your email from the review site, as it will contain the updates for your submission. - -8. If the code is approved with a +2 review, Gerrit will automatically merge your code. - - -File Bugs ---------- - -Bugs should be filed on Launchpad, not GitHub: - - https://bugs.launchpad.net/vmtp - - -Build VMTP Docker Image ------------------------ - -Two files are used to build the Docker image: *Dockerfile* and *.dockerignore*. The former provides all the build instructions while the latter provides the list of files/directories that should not be copied to the Docker image. - -In order to make the Docker image clean, remove all auto generated files from the root of your workspace first. It is strongly recommeneded to simply pull a new one from GitHub/StackForge. Specify the image name and the tag, and feed them to docker build. Examples to build the image with name "$USER/vmtp", tag "2.0.0" and "latest":: - - $ cd - $ sudo docker build --tag=$USER/vmtp:2.0.0 . - $ sudo docker build --tag=$USER/vmtp:latest . - -The images should be available for use:: - - $ sudo docker images - REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE - ahothan/vmtp 2.0.0 9f08056496d7 27 hours ago 494.6 MB - ahothan/vmtp latest 9f08056496d7 27 hours ago 494.6 MB - -For exchanging purposes, the image could be saved to a tar archive. You can distribute the VMTP Docker image among your servers easily with this feature:: - - $ sudo docker save -o - -To publish the image to Docker Hub:: - - $ sudo docker login - $ sudo docker push $USER/vmtp:2.0.0 - $ sudo docker push $USER/vmtp:latest - - -.. _developer_guide_of_openstack: - -Developer's Guide of OpenStack ------------------------------- - -If you would like to contribute to the development of OpenStack, you must follow the steps in this page: - - http://docs.openstack.org/infra/manual/developers.html - -Once those steps have been completed, changes to OpenStack should be submitted for review via the Gerrit tool, following the workflow documented at: - - http://docs.openstack.org/infra/manual/developers.html#development-workflow - -Pull requests submitted through GitHub will be ignored. - +.. include:: ../../CONTRIBUTING.rst diff --git a/doc/source/images/flows.png b/doc/source/images/flows.png deleted file mode 100644 index 8bd3b14beba48ddf7f739ed261e3c07e1b5d4cd8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47456 zcmeFZWmMcv(%0D&upzoiH*(k~XRB-_cQfezGlZ)FsnUiy} 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 z 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 diff --git a/doc/source/implementation.rst b/doc/source/implementation.rst deleted file mode 100644 index 7203b10..0000000 --- a/doc/source/implementation.rst +++ /dev/null @@ -1,19 +0,0 @@ -============== -Implementation -============== - -TCP Throughput Measurement --------------------------- - -The TCP throughput reported is measured using the default message size of the test tool (64KB with nuttcp). The TCP MSS (maximum segment size) used is the one suggested by the TCP-IP stack (which is dependent on the MTU). - - -UDP Throughput Measurement --------------------------- -UDP throughput is tricky because of limitations of the performance tools used, limitations of the Linux kernel used and criteria for finding the throughput to report. - -The default setting is to find the "optimal" throughput with packet loss rate within the 2%~5% range. This is achieved by successive iterations at different throughput values. - -In some cases, it is not possible to converge with a loss rate within that range and trying to do so may require too many iterations. The algorithm used is empiric and tries to achieve a result within a reasonable and bounded number of iterations. In most cases the optimal throughput is found in less than 30 seconds for any given flow. - -**Note:** UDP measurements are only available with nuttcp (not available with iperf). diff --git a/doc/source/index.rst b/doc/source/index.rst index 62cc978..d7c9cdf 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1,10 +1,10 @@ -.. vmtp documentation master file, created by - sphinx-quickstart on Fri Feb 13 14:43:59 2015. +.. kloudbuster documentation master file, created by + sphinx-quickstart on Tue Jul 9 22:26:36 2013. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to vmtp's documentation! -================================ +Welcome to kloudbuster's documentation! +======================================================== Contents: @@ -14,15 +14,12 @@ Contents: readme installation usage - setup - implementation - issue contributing -.. Indices and tables -.. ================== +Indices and tables +================== -.. * :ref:`genindex` -.. * :ref:`modindex` -.. * :ref:`search` +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/doc/source/installation.rst b/doc/source/installation.rst index 489f469..deb4380 100644 --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -2,118 +2,11 @@ Installation ============ -There are two ways to install and run VMTP tool, Docker based, and GitHub/StackForge Repository based. Normally, a VMTP Docker image will satisfy most of use cases, and it is easy to start and use. Docker image is recommended if running under a production environment, or running through an automated or scheduled job. A git repository based installation gives more flexibility, and it is recommended for developing purposes. +At the command line:: + $ pip install kloudbuster -Docker based Installation -------------------------- +Or, if you have virtualenvwrapper installed:: -Docker provides an easy and convenient way to run VMTP on Linux. The docker image pre-builds all the dependencies needed to run VMTP, including all the OpenStack python client libraries needed to access any OpenStack cloud, all the dependent python libraries and all the dependent distribution packages needed by these python libraries. - -To run the container image all you need is docker.io installed on your Linux host. Refer `here `_ for details about how to install docker. - -**Note:** An official image from Docker Hub is coming soon and this is a temporary private image. - -Once the docker.io is installed, download the latest VMTP image from Docker Hub:: - - $ sudo docker pull ahothan/vmtp - -The new image will be shown in the list:: - - $ sudo docker images - REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE - ahothan/vmtp 2.0.0 9f08056496d7 27 hours ago 494.6 MB - ahothan/vmtp latest 9f08056496d7 27 hours ago 494.6 MB - -Alternatively, for development or test purpose, a binary image could be loaded from a filesystem as well:: - - $ sudo docker load -i vmtp_image - -Note that the image loaded from archive doesn't have a TAG, so the exact image ID must be specified to all docker commands mentioned below. - -In its Docker image form, VMTP is located under the /vmtp directory in the container and can either take arguments from the host shell, or can be executed from inside the Docker image shell. - -To run VMTP directly from the host shell:: - - $ sudo docker run python /vmtp/vmtp.py - -To run VMTP from the Docker image shell:: - - $ sudo docker run - $ cd /vmtp.py - $ python vmtp.py - -(then type exit to exit and terminate the container instance) - - -Docker Shared Volume to Share Files with the Container -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -VMTP can accept files as input (e.g. configuration and openrc file) and can generate json results into a file. It is possible to use the VMTP Docker image with files persisted on the host by using Docker shared volumes. - -For example, to get a copy of the VMTP default configuration file from the container:: - - $ sudo docker run -v $PWD:/vmtp/shared:rw -t cp /vmtp/cfg.default.yaml /vmtp/shared/mycfg.yaml - -The local directory to share ($PWD) is to be mapped to /vmtp/shared in the container in read/write mode. That way, mycfg.yaml will be copied to the local directory on the host. - -Assume you have edited the configuration file "mycfg.yaml", retrieved an openrc file "admin-openrc.sh" from Horizon on the local directory, and would like to get results back in the "res.json" file. What you can do is to map the current directory ($PWD) to /vmtp/shared inside the container in read/write mode, then run the script inside the container and use use files from the shared directory. - -E.g. From the host shell, you could do that in one-shot:: - - $ sudo docker run -v $PWD:/vmtp/shared:rw -t python /vmtp/vmtp.py -c shared/mycfg.yaml -r shared/admin-openrc.sh -p admin --json shared/res.json - $ cat res.json - -Or from the Docker image shell:: - - $ sudo docker run -v $PWD:/vmtp/shared:rw -t - $ python /vmtp/vmtp.py -c shared/mycfg.yaml -r shared/admin-openrc.sh -p admin --json shared/res.json - $ cat shared/res.json - - -.. _git_installation: - -GitHub/StackForge Repository based Installation ------------------------------------------------ - -It is recommended to run VMTP inside a virtual environment. However, it can be skipped if installed in a dedicated VM. - - -Super quick installation on Ubuntu/Debian -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: - - $ sudo apt-get install python-dev python-virtualenv git git-review - $ sudo apt-get install libxml2-dev libxslt-dev libffi-dev libz-dev libyaml-dev libssl-dev - $ # create a virtual environment - $ virtualenv ./vmtpenv - $ source ./vmtpenv/bin/activate - $ git clone git://git.openstack.org/stackforge/vmtp - $ cd vmtp - $ pip install -r requirements-dev.txt - $ python vmtp.py -h - - -Super quick installation on MacOSX -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -VMTP can run natively on MacOSX. These instructions have been verified to work on MacOSX 10.10 (Yosemite). - -First, download XCode from App Store, then execute below commands: - -.. code:: - - $ # Download the XCode command line tools - $ code-select --install - $ # Install pip - $ sudo easy_install pip - $ # Install python virtualenv - $ sudo pip install virtualenv - $ # create a virtual environment - $ virtualenv ./vmtpenv - $ source ./vmtpenv/bin/activate - $ git clone git://git.openstack.org/stackforge/vmtp - $ cd vmtp - $ pip install -r requirements-dev.txt - $ python vmtp.py -h + $ mkvirtualenv kloudbuster + $ pip install kloudbuster diff --git a/doc/source/issue.rst b/doc/source/issue.rst deleted file mode 100644 index 396d458..0000000 --- a/doc/source/issue.rst +++ /dev/null @@ -1,9 +0,0 @@ -======================== -Caveats and Known Issues -======================== - -* UDP throughput is not available if iperf is selected (the iperf UDP reported results are not reliable enough for iterating) - -* If VMTP hangs for native hosts throughputs, check firewall rules on the hosts to allow TCP/UDP ports 5001 and TCP port 5002 - -* When storing the results to JSON or MongoDB, the quotes in the command-line will not be saved. In a unix-like environment, the magic happened even before Python can see them. e.g. quotes get consumed, variables get interpolated, etc. Keep this in mind when you want to execute the command stored in "*args*", and pay more attention in any parameter that may have quotes inside like *test_description*. diff --git a/doc/source/setup.rst b/doc/source/setup.rst deleted file mode 100644 index 3edfea9..0000000 --- a/doc/source/setup.rst +++ /dev/null @@ -1,33 +0,0 @@ -===== -Setup -===== - - -SSH Authentication ------------------- - -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. - - - - diff --git a/doc/source/usage.rst b/doc/source/usage.rst index 51a0207..5bdc23e 100644 --- a/doc/source/usage.rst +++ b/doc/source/usage.rst @@ -1,321 +1,7 @@ -===== +======== Usage -===== +======== -VMTP Usage ----------- - -.. code:: - - usage: vmtp.py [-h] [-c ] [-r ] - [-m [:]] [-p ] [-t