diff --git a/doc/samples/tasks/scenarios/vm/boot-runperf-delete.json b/doc/samples/tasks/scenarios/vm/boot-runperf-delete.json
new file mode 100644
index 0000000..27ea699
--- /dev/null
+++ b/doc/samples/tasks/scenarios/vm/boot-runperf-delete.json
@@ -0,0 +1,33 @@
+{
+    "VMPerf.boot_runperf_delete": [
+        {
+            "args": {
+                "flavor": {
+                    "name": "m1.small"
+                },
+                "image": {
+                    "name": "Ubuntu Server 14.04"
+                },
+                "src_file": "doc/samples/tasks/support/tools/iperf",
+                "dst_file": "/tmp/iperf",
+                "floating_network": "net04_ext",
+                "use_floatingip": true,
+                "username": "ubuntu"
+            },
+            "runner": {
+                "type": "constant",
+                "times": 4,
+                "concurrency": 2 
+            },
+            "context": {
+                "neutron_network": {
+                    "network_cidr": "10.%s.0.0/16",
+                },
+                "users": {
+                    "tenants": 1,
+                    "users_per_tenant": 1 
+                }
+            }
+        }
+    ]
+}
diff --git a/doc/samples/tasks/support/tools/iperf b/doc/samples/tasks/support/tools/iperf
new file mode 100755
index 0000000..acf5ea9
Binary files /dev/null and b/doc/samples/tasks/support/tools/iperf differ
diff --git a/rally/benchmark/scenarios/vm/utils.py b/rally/benchmark/scenarios/vm/utils.py
index 3d7a71d..bd363ce 100644
--- a/rally/benchmark/scenarios/vm/utils.py
+++ b/rally/benchmark/scenarios/vm/utils.py
@@ -58,6 +58,24 @@ class VMScenario(base.Scenario):
         self.wait_for_ssh(ssh)
         return self.run_action(ssh, interpreter, script)
 
+    def run_cmd_single(self, server_ip, port, username,cmd):
+       """ Run command via SSH on server
+       Run a single command at a time
+       """
+       self.wait_for_ping(server_ip)
+       ssh = sshutils.SSH(username, server_ip, port=port,
+                           pkey=self.context()["user"]["keypair"]["private"])
+       self.wait_for_ssh(ssh)
+       return ssh.execute(cmd)
+    
+    def run_cmd_upload(self, server_ip, port, username, src_file, dst_file):
+        self.wait_for_ping(server_ip)
+        ssh = sshutils.SSH(username, server_ip, port=port,
+                            pkey=self.context()["user"]["keypair"]["private"])
+        self.wait_for_ssh(ssh)
+        cmd = "sudo cat > " + dst_file
+        ssh.run(cmd, stdin= open(src_file, 'rb+'))
+
     @staticmethod
     def check_network(server, network):
         """Check if a server is attached to the specified network.
diff --git a/rally/benchmark/scenarios/vm/vm_perf.py b/rally/benchmark/scenarios/vm/vm_perf.py
new file mode 100644
index 0000000..afeac2f
--- /dev/null
+++ b/rally/benchmark/scenarios/vm/vm_perf.py
@@ -0,0 +1,234 @@
+# Copyright 2014: Rackspace UK
+# 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.
+
+import json
+import re 
+from rally.benchmark.scenarios import base
+from rally.benchmark.scenarios.nova import utils as nova_utils
+from rally.benchmark.scenarios.vm import utils as vm_utils
+from rally.benchmark import types as types
+from rally.benchmark import validation
+from rally import consts
+from rally import exceptions
+
+
+MULTIPLIERS = {
+    'Kbits/sec': 1.0e3,
+    'Mbits/sec': 1.0e6,
+    'Gbits/sec': 1.0e9,
+    }
+
+class VMPerf(nova_utils.NovaScenario, vm_utils.VMScenario):
+
+    def __init__(self, *args, **kwargs):
+        super(VMPerf, self).__init__(*args, **kwargs)
+
+    @types.set(image=types.ImageResourceType,
+               flavor=types.FlavorResourceType)
+    @(validation.image_valid_on_flavor("flavor", "image")
+    @(validation.number("port", minval=1, maxval=65535,
+                                      nullable=True, integer_only=True)
+    @(validation.external_network_exists("floating_network",
+                                                       "use_floatingip")
+    @base.scenario(
+        context={"cleanup": ["nova"], "keypair": {}, "allow_ssh": {}})
+    @validation.required_services(consts.Service.NOVA)
+    def boot_runperf_delete(self, image, flavor,
+                               username,
+                               src_file, dst_file,
+                               floating_network="public",
+                               ip_version=4, port=22,
+                               use_floatingip=True, **kwargs):
+        """Boot server, run a script that outputs JSON, delete server.
+        :param username: User to SSH to instance as
+        :param floating_network: External network used to get floating ip from
+        :param ip_version: Version of ip protocol to use for connection
+        :param port: Port to use for SSH connection
+        :param use_floatingip: Whether to associate a floating ip for
+                connection
+
+        :returns: Dictionary containing two keys, data and errors. Data is JSON
+                 data output by the script. Errors is raw data from the
+                 script's standard error stream.
+        """
+        server = None
+        client = None
+        floating_ip_server = None
+        floating_ip_client = None
+        try:
+            server = self._boot_server(
+                self._generate_random_name("rally_novaserver_server"),
+                image, flavor, key_name='rally_ssh_key', **kwargs)
+          
+            
+            client = self._boot_server(
+                self._generate_random_name("rally_novaserver_client"),
+                image, flavor, key_name='rally_ssh_key', **kwargs)
+           
+
+            #Create floating ip for server VM
+            floating_ip_server = self._create_floating_ip(floating_network)
+            self._associate_floating_ip(server, floating_ip_server)
+            server_ip = floating_ip_server.ip
+            #Create floating ip for client VM
+            floating_ip_client = self._create_floating_ip(floating_network)
+            self._associate_floating_ip(client, floating_ip_client)
+            client_ip = floating_ip_client.ip
+
+            self.run_cmd_upload(server_ip, port, username, src_file, dst_file)
+
+            cmd = "sudo chmod 755 /tmp/iperf"
+            code_server, out_server, err_server = self.run_cmd_single(server_ip, port,
+                                             username, cmd)
+            if code_server:
+                raise Exception("chmod command failed")
+
+	    cmd = "sudo /tmp/iperf -s -D > /dev/null"
+                  
+            code_server, out_server, err_server = self.run_cmd_single(server_ip, port,
+                                              username, cmd)  
+            if code_server:
+                raise Exception("Server iperf start failed")
+    
+            self.run_cmd_upload(client_ip, port, username, src_file, dst_file)
+            cmd = "sudo chmod 755 /tmp/iperf"
+            code_client, out_client, err_client = self.run_cmd_single(client_ip, port,
+                                               username, cmd)
+            
+	    cmd = "sudo /tmp/iperf -c" + " " + server_ip
+	    code_client, out_client, err_client = self.run_cmd_single(client_ip, port,
+                                              username, cmd) 
+	    if code_client:
+                raise Exception("Client iperf command failed")
+
+            iperf_op = self._parse_iperf_output(out_client)
+
+        # Always try to free resources
+        finally:
+            if use_floatingip:
+                self._release_server_floating_ip(server, floating_ip_server)
+		self._release_server_floating_ip(client, floating_ip_client)
+            if server:
+                self._delete_server(server)
+	    if client:
+                self._delete_server(client)
+
+        return {"data": iperf_op, "errors": err_client}
+
+    def _release_server_floating_ip(self, server, floating_ip):
+        """Release a floating ip associated to a server.
+
+        This method check that the given floating ip is associated with the
+        specified server and tries to dissociate it.
+        Once dissociated, release the floating ip to reintegrate
+        it to the pool of available ips.
+
+        :param server: The server to dissociate the floating ip from
+        :param floating_ip: The floating ip to release
+        """
+        if floating_ip and server:
+            if self.check_ip_address(floating_ip)(server):
+                self._dissociate_floating_ip(server, floating_ip)
+        if floating_ip:
+            self._delete_floating_ip(floating_ip)
+
+    def _parse_iperf_output(self,input_str):
+        ''' function that parses i-perf output'''
+
+        err_msg = ": Cannot Parse Iperf output"
+        if not isinstance(input_str, list):
+            lines = input_str.split('\n')
+            all_text = input_str
+        else:
+            lines = input_str
+            all_text = '\n'.join(lines)
+
+        if 'Connection refused' in all_text:
+            print 'Could not connect to iperf server'
+            return err_msg
+
+        if not re.search("Client connecting", all_text):
+            print 'Could not connect to iperf server'
+            return err_msg
+
+        if 'TCP' in lines[1]:
+            protocol = 'TCP'
+        elif 'UDP' in lines[1]:
+            protocol = 'UDP'
+        else:
+            print '%s: Could not parse header line %s' % (func_name, lines[1])
+            return err_msg
+        if protocol == 'TCP':
+            '''Parses the following and returns a single element dictionary
+            with throughput value.
+
+            ------------------------------------------------------------
+            Client connecting to localhost, TCP port 5001
+            TCP window size: 49.4 KByte (default)
+            ------------------------------------------------------------
+            [  3] local 127.0.0.1 port 57936 connected with 127.0.0.1 port 5001
+            [ ID] Interval       Transfer     Bandwidth
+            [  3]  0.0-10.0 sec  2.09 GBytes  1.79 Gbits/sec
+            '''
+            tcp_tokens = lines[6].split()
+            if len(tcp_tokens) >= 6 and tcp_tokens[-1] in MULTIPLIERS:
+                return {'throughput':
+                        MULTIPLIERS[tcp_tokens[-1]] * float(tcp_tokens[-2])}
+            else:
+                print '%s: Could not parse throughput line: %s' \
+                    % (func_name, lines[6])
+        elif protocol == 'UDP':
+            '''
+            Parses the following and returns a dictionary of
+            performance values.
+
+            ------------------------------------------------------------
+            Client connecting to localhost, UDP port 5001
+            Sending 1470 byte datagrams
+            UDP buffer size:   108 KByte (default)
+            ------------------------------------------------------------
+            [  3] local 127.0.0.1 port 54244 connected with 127.0.0.1 port 5001
+            [ ID] Interval       Transfer     Bandwidth
+            [  3]  0.0-10.0 sec  1.25 MBytes  1.05 Mbits/sec
+            [  3] Sent 893 datagrams
+            [  3] Server Report:
+            [ ID] Interval       Transfer     Bandwidth       Jitter   Lost/Total Da
+            [  3]  0.0-10.0 sec  1.25 MBytes  1.05 Mbits/sec  0.032 ms 1/894 (0.11%)
+            [  3]  0.0-15.0 sec  14060 datagrams received out-of-order
+            '''
+            # Search for the last row containing the word 'Bytes'
+            mb_row = [row for row, data in enumerate(lines) \
+                if 'Bytes' in data][-1]
+            udp_tokens = lines[mb_row].replace('/', ' ').split()
+            # Find the column ending with "...Bytes"
+            mb_col = [col for col, data in enumerate(udp_tokens) \
+                if data.endswith('Bytes')]
+            if len(mb_col) > 0 and len(udp_tokens) >= mb_col[0] + 9:
+                # Make a sublist starting after the column named "MBytes"
+                stat_tokens = udp_tokens[mb_col[0]+1:]
+                # Rebuild Mbits/sec out of Mbits sec
+                multiplier = '%s/%s' % tuple(stat_tokens[1:3])
+                if multiplier not in MULTIPLIERS:
+                    print 'Could not parse multiplier in %s' % mb_row
+                return {'throughput':
+                        float(stat_tokens[0]) * MULTIPLIERS[multiplier],
+                    'jitter':float(stat_tokens[3]),
+                    'lost':float(stat_tokens[7].strip('()%'))}
+            else:
+                print '%s: Could not parse UDP test output: %s' \
+                    % (func_name, lines)
+        else:
+            print '%s: Unhandled protocol %s' % (func_name, lines)
+        return err_msg
