Merge "Fix update option for run_tests.sh"
diff --git a/stress/driver.py b/stress/driver.py
index c50e957..8dc88cf 100644
--- a/stress/driver.py
+++ b/stress/driver.py
@@ -24,9 +24,10 @@
 from state import FloatingIpState
 from state import KeyPairState
 from state import VolumeState
-from test_case import *
+import stress.utils
+from test_case import logging
+
 from tempest.common.utils.data_utils import rand_name
-import utils.util
 
 # setup logging to file
 logging.basicConfig(
@@ -73,7 +74,7 @@
     if keypath is None or user is None:
         return nodes
     cmd = "nova-manage service list | grep ^nova-compute"
-    lines = utils.util.ssh(keypath, user, controller, cmd).split('\n')
+    lines = stress.utils.ssh(keypath, user, controller, cmd).split('\n')
     # For example: nova-compute xg11eth0 nova enabled :-) 2011-10-31 18:57:46
     # This is fragile but there is, at present, no other way to get this info.
     for line in lines:
@@ -89,7 +90,7 @@
     """
     grep = 'egrep "ERROR\|TRACE" %s/*.log' % logdir
     for node in nodes:
-        errors = utils.util.ssh(keypath, user, node, grep, check=False)
+        errors = stress.utils.ssh(keypath, user, node, grep, check=False)
         if len(errors) > 0:
             logging.error('%s: %s' % (node, errors))
             return True
@@ -173,8 +174,8 @@
     user = stress_config.host_admin_user
     logdir = stress_config.nova_logdir
     computes = _get_compute_nodes(keypath, user, manager.config.identity.host)
-    utils.util.execute_on_all(keypath, user, computes,
-                              "rm -f %s/*.log" % logdir)
+    stress.utils.execute_on_all(keypath, user, computes,
+                                "rm -f %s/*.log" % logdir)
     random.seed(seed)
     cases = _create_cases(choice_spec)
     state = ClusterState(max_vms=max_vms)
diff --git a/stress/state.py b/stress/state.py
index 3a9f12e..9c31b76 100644
--- a/stress/state.py
+++ b/stress/state.py
@@ -16,7 +16,8 @@
 class ClusterState(object):
     """A class to store the state of various persistent objects in the Nova
     cluster, e.g. instances, volumes.  Use methods to query to state which than
-    can be compared to the current state of the objects in Nova"""
+    can be compared to the current state of the objects in Nova.
+    """
 
     def __init__(self, **kwargs):
         self._max_vms = kwargs.get('max_vms', 32)
@@ -84,7 +85,8 @@
 
 class ServerAssociatedState(object):
     """Class that tracks resources that are associated with a particular server
-    such as a volume or floating ip"""
+    such as a volume or floating ip.
+    """
 
     def __init__(self, resource_id):
         # The id of the server.
diff --git a/stress/test_floating_ips.py b/stress/test_floating_ips.py
index 97e4382..6774e81 100755
--- a/stress/test_floating_ips.py
+++ b/stress/test_floating_ips.py
@@ -14,7 +14,6 @@
 
 import random
 import telnetlib
-import time
 
 import pending_action
 import test_case
diff --git a/stress/test_server_actions.py b/stress/test_server_actions.py
index a2032f0..f4ddf23 100644
--- a/stress/test_server_actions.py
+++ b/stress/test_server_actions.py
@@ -18,12 +18,11 @@
 actions veriy that the API call was successful or not."""
 
 import random
-import time
 
 import pending_action
+import stress.utils
 from tempest.exceptions import Duplicate
 import test_case
-from utils.util import *
 
 
 class TestRebootVM(test_case.StressTestCase):
@@ -81,7 +80,7 @@
 
 class VerifyRebootVM(pending_action.PendingServerAction):
     """Class to verify that the reboot completed."""
-    States = enum('REBOOT_CHECK', 'ACTIVE_CHECK')
+    States = stress.utils.enum('REBOOT_CHECK', 'ACTIVE_CHECK')
 
     def __init__(self, manager, state, target_server,
                  reboot_state=None,
diff --git a/stress/test_servers.py b/stress/test_servers.py
index e1cb4d1..25cbbb0 100644
--- a/stress/test_servers.py
+++ b/stress/test_servers.py
@@ -18,7 +18,6 @@
 actions veriy that the API call was successful or not."""
 
 import random
-import time
 
 import pending_action
 import test_case
@@ -39,7 +38,6 @@
         `pargs`      : positional arguments
         `kwargs`     : keyword arguments, which include:
                        `key_name`  : name of keypair
-                       `timeout`   : how long to wait before issuing Exception
                        `image_ref` : index to image types availablexs
                        `flavor_ref`: index to flavor types available
                                      (default = 1, which is tiny)
@@ -52,8 +50,6 @@
             return None
 
         _key_name = kwargs.get('key_name', '')
-        _timeout = int(kwargs.get('timeout',
-                                  manager.config.compute.build_timeout))
         _image_ref = kwargs.get('image_ref', manager.config.compute.image_ref)
         _flavor_ref = kwargs.get('flavor_ref',
                                  manager.config.compute.flavor_ref)
diff --git a/stress/tests/create_kill.py b/stress/tests/create_kill.py
index 2565723..30ddfd7 100644
--- a/stress/tests/create_kill.py
+++ b/stress/tests/create_kill.py
@@ -14,9 +14,13 @@
 """More aggressive test that creates and destroys VMs with shorter
 sleep times"""
 
-from stress.test_servers import *
+import datetime
+import time
+
 from stress.basher import BasherAction
-from stress.driver import *
+from stress.driver import bash_openstack
+from stress.test_servers import TestCreateVM
+from stress.test_servers import TestKillActiveVM
 from tempest import clients
 
 choice_spec = [
diff --git a/stress/tests/floating_ips.py b/stress/tests/floating_ips.py
index 6a4452c..b1b3778 100755
--- a/stress/tests/floating_ips.py
+++ b/stress/tests/floating_ips.py
@@ -13,8 +13,10 @@
 #    limitations under the License.
 """Stress test that associates/disasssociates floating ips."""
 
+import datetime
+
 from stress.basher import BasherAction
-from stress.driver import *
+from stress.driver import bash_openstack
 from stress.test_floating_ips import TestChangeFloatingIp
 from tempest import clients
 
diff --git a/stress/tests/hard_reboots.py b/stress/tests/hard_reboots.py
index fe57be1..50a2e91 100644
--- a/stress/tests/hard_reboots.py
+++ b/stress/tests/hard_reboots.py
@@ -13,11 +13,13 @@
 #    limitations under the License.
 """Test that reboots random instances in a Nova cluster."""
 
+import datetime
+import time
 
-from stress.test_servers import *
-from stress.test_server_actions import *
 from stress.basher import BasherAction
-from stress.driver import *
+from stress.driver import bash_openstack
+from stress.test_server_actions import TestRebootVM
+from stress.test_servers import TestCreateVM
 from tempest import clients
 
 choice_spec = [
diff --git a/stress/tests/user_script_sample.py b/stress/tests/user_script_sample.py
index 04163e3..d941ea0 100644
--- a/stress/tests/user_script_sample.py
+++ b/stress/tests/user_script_sample.py
@@ -14,10 +14,12 @@
 """Sample stress test that creates a few virtual machines and then
 destroys them"""
 
+import datetime
 
-from stress.test_servers import *
 from stress.basher import BasherAction
-from stress.driver import *
+from stress.driver import bash_openstack
+from stress.test_servers import TestCreateVM
+from stress.test_servers import TestKillActiveVM
 from tempest import clients
 
 choice_spec = [
diff --git a/stress/utils/util.py b/stress/utils.py
similarity index 100%
rename from stress/utils/util.py
rename to stress/utils.py
diff --git a/stress/utils/__init__.py b/stress/utils/__init__.py
deleted file mode 100644
index 85300f2..0000000
--- a/stress/utils/__init__.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2011 Quanta Research Cambridge, Inc.
-#
-#    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.
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index faac1a0..45737be 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -129,7 +129,7 @@
         LOG.debug('\n'.join(dump))
 
     def _http_request(self, url, method, **kwargs):
-        """ Send an http request with the specified characteristics.
+        """Send an http request with the specified characteristics.
 
         Wrapper around httplib.HTTP(S)Connection.request to handle tasks such
         as setting headers and error handling.
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index e163126..170a137 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -80,7 +80,7 @@
     def clear_auth(self):
         """
         Can be called to clear the token and base_url so that the next request
-        will fetch a new token and base_url
+        will fetch a new token and base_url.
         """
 
         self.token = None
@@ -88,7 +88,8 @@
 
     def get_auth(self):
         """Returns the token of the current request or sets the token if
-        none"""
+        none.
+        """
 
         if not self.token:
             self._set_auth()
@@ -97,7 +98,7 @@
 
     def basic_auth(self, user, password, auth_url):
         """
-        Provides authentication for the target API
+        Provides authentication for the target API.
         """
 
         params = {}
@@ -114,7 +115,7 @@
 
     def keystone_auth(self, user, password, auth_url, service, tenant_name):
         """
-        Provides authentication via Keystone
+        Provides authentication via Keystone.
         """
 
         # Normalize URI to ensure /tokens is in it.
diff --git a/tempest/openstack/common/cfg.py b/tempest/openstack/common/cfg.py
index 08d9b47..cae4ecc 100644
--- a/tempest/openstack/common/cfg.py
+++ b/tempest/openstack/common/cfg.py
@@ -1437,7 +1437,7 @@
         logger.log(lvl, "=" * 80)
 
         def _sanitize(opt, value):
-            """Obfuscate values of options declared secret"""
+            """Obfuscate values of options declared secret."""
             return value if not opt.secret else '*' * len(str(value))
 
         for opt_name in sorted(self._opts):
diff --git a/tempest/openstack/common/iniparser.py b/tempest/openstack/common/iniparser.py
index 9bf399f..9a8762a 100644
--- a/tempest/openstack/common/iniparser.py
+++ b/tempest/openstack/common/iniparser.py
@@ -100,15 +100,15 @@
             self._assignment(key, value)
 
     def assignment(self, key, value):
-        """Called when a full assignment is parsed"""
+        """Called when a full assignment is parsed."""
         raise NotImplementedError()
 
     def new_section(self, section):
-        """Called when a new section is started"""
+        """Called when a new section is started."""
         raise NotImplementedError()
 
     def comment(self, comment):
-        """Called when a comment is parsed"""
+        """Called when a comment is parsed."""
         pass
 
     def error_invalid_assignment(self, line):
diff --git a/tempest/openstack/common/setup.py b/tempest/openstack/common/setup.py
index 35680b3..2fb9cf2 100644
--- a/tempest/openstack/common/setup.py
+++ b/tempest/openstack/common/setup.py
@@ -277,7 +277,8 @@
 def _get_version_from_git(pre_version):
     """Return a version which is equal to the tag that's on the current
     revision if there is one, or tag plus number of additional revisions
-    if the current revision has no tag."""
+    if the current revision has no tag.
+    """
 
     if os.path.isdir('.git'):
         if pre_version:
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index 02cf970..ecff7be 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -498,3 +498,11 @@
         resp, body = self.post(url, post_body, self.headers)
         body = json.loads(body)
         return resp, body['output']
+
+    def list_virtual_interfaces(self, server_id):
+        """
+        List the virtual interfaces used in an instance.
+        """
+        resp, body = self.get('/'.join(['servers', server_id,
+                              'os-virtual-interfaces']))
+        return resp, json.loads(body)
diff --git a/tempest/services/compute/xml/common.py b/tempest/services/compute/xml/common.py
index 6469761..bbc4e38 100644
--- a/tempest/services/compute/xml/common.py
+++ b/tempest/services/compute/xml/common.py
@@ -96,7 +96,8 @@
     """This does a really braindead conversion of an XML tree to
     something that looks like a json dump. In cases where the XML
     and json structures are the same, then this "just works". In
-    others, it requires a little hand-editing of the result."""
+    others, it requires a little hand-editing of the result.
+    """
     json = {}
     for attr in node.keys():
         json[attr] = node.get(attr)
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index 8be342d..3abf0c3 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -55,7 +55,7 @@
 
 
 def _translate_server_xml_to_json(xml_dom):
-    """ Convert server XML to server JSON.
+    """Convert server XML to server JSON.
 
     The addresses collection does not convert well by the dumb xml_to_json.
     This method does some pre and post-processing to deal with that.
@@ -393,3 +393,12 @@
                                body=str(Document(post_body)))
         body = xml_to_json(etree.fromstring(body))
         return resp, body
+
+    def list_virtual_interfaces(self, server_id):
+        """
+        List the virtual interfaces used in an instance.
+        """
+        resp, body = self.get('/'.join(['servers', server_id,
+                              'os-virtual-interfaces']), self.headers)
+        server = self._parse_server(etree.fromstring(body))
+        return resp, server
diff --git a/tempest/services/volume/xml/snapshots_client.py b/tempest/services/volume/xml/snapshots_client.py
index dc70cb7..89ea89f 100644
--- a/tempest/services/volume/xml/snapshots_client.py
+++ b/tempest/services/volume/xml/snapshots_client.py
@@ -70,7 +70,7 @@
         return resp, xml_to_json(body)
 
     def create_snapshot(self, volume_id, **kwargs):
-        """ Creates a new snapshot.
+        """Creates a new snapshot.
         volume_id(Required): id of the volume.
         force: Create a snapshot even if the volume attached (Default=False)
         display_name: Optional snapshot Name.
diff --git a/tempest/test.py b/tempest/test.py
index 58fadeb..616a866 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -31,7 +31,8 @@
 
     This decorator applies the nose attr decorator as well as the
     the testtools.testcase.attr if it is in the list of attributes
-    to testtools we want to apply."""
+    to testtools we want to apply.
+    """
 
     def decorator(f):
         testtool_attributes = ('smoke')
diff --git a/tempest/testboto.py b/tempest/testboto.py
index 5ce15b7..30c7e93 100644
--- a/tempest/testboto.py
+++ b/tempest/testboto.py
@@ -144,7 +144,8 @@
     @classmethod
     def addResourceCleanUp(cls, function, *args, **kwargs):
         """Adds CleanUp callable, used by tearDownClass.
-        Recommended to a use (deep)copy on the mutable args"""
+        Recommended to a use (deep)copy on the mutable args.
+        """
         cls._sequence = cls._sequence + 1
         cls._resource_trash_bin[cls._sequence] = (function, args, kwargs)
         return cls._sequence
@@ -161,7 +162,8 @@
             self.assertBotoError(self.ec2_error_code.client.
                                  InvalidKeyPair.Duplicate,
                                  self.client.create_keypair,
-                                 key_name)"""
+                                 key_name)
+        """
         try:
             callableObj(*args, **kwargs)
         except exception.BotoServerError as exc:
@@ -173,8 +175,9 @@
 
     @classmethod
     def tearDownClass(cls):
-        """ Calls the callables added by addResourceCleanUp,
-        when you overwire this function dont't forget to call this too"""
+        """Calls the callables added by addResourceCleanUp,
+        when you overwire this function dont't forget to call this too.
+        """
         fail_count = 0
         trash_keys = sorted(cls._resource_trash_bin, reverse=True)
         for key in trash_keys:
diff --git a/tempest/tests/compute/servers/test_create_server.py b/tempest/tests/compute/servers/test_create_server.py
index 54f1131..838b382 100644
--- a/tempest/tests/compute/servers/test_create_server.py
+++ b/tempest/tests/compute/servers/test_create_server.py
@@ -17,6 +17,7 @@
 
 import base64
 
+import netaddr
 import testtools
 
 
@@ -36,10 +37,7 @@
     def setUpClass(cls):
         cls.meta = {'hello': 'world'}
         cls.accessIPv4 = '1.1.1.1'
-        cls.accessIPv6 = '::babe:220.12.22.2'
-        # See: http://tools.ietf.org/html/rfc5952 (section 4)
-        # This is the canonicalized form of the above.
-        cls.accessIPv6canon = '::babe:dc0c:1602'
+        cls.accessIPv6 = '0000:0000:0000:0000:0000:babe:220.12.22.2'
         cls.name = rand_name('server')
         file_contents = 'This is a test file.'
         personality = [{'path': '/test.txt',
@@ -73,8 +71,10 @@
     def test_verify_server_details(self):
         # Verify the specified server attributes are set correctly
         self.assertEqual(self.accessIPv4, self.server['accessIPv4'])
-        self.assertIn(self.server['accessIPv6'],
-                      [self.accessIPv6, self.accessIPv6canon])
+        # NOTE(maurosr): See http://tools.ietf.org/html/rfc5952 (section 4)
+        # Here we compare directly with the canonicalized format.
+        self.assertEqual(self.server['accessIPv6'],
+                         str(netaddr.IPAddress(self.accessIPv6)))
         self.assertEqual(self.name, self.server['name'])
         self.assertEqual(self.image_ref, self.server['image']['id'])
         self.assertEqual(str(self.flavor_ref), self.server['flavor']['id'])
diff --git a/tempest/tests/compute/servers/test_virtual_interfaces.py b/tempest/tests/compute/servers/test_virtual_interfaces.py
new file mode 100644
index 0000000..6198526
--- /dev/null
+++ b/tempest/tests/compute/servers/test_virtual_interfaces.py
@@ -0,0 +1,87 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack Foundation
+# 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.
+
+from tempest.common.utils.data_utils import rand_name
+from tempest import exceptions
+from tempest.test import attr
+from tempest.tests.compute import base
+
+
+class VirtualInterfacesTest(object):
+
+    @classmethod
+    def setUpClass(self, cls):
+        cls.name = rand_name('server')
+        cls.client = cls.servers_client
+        resp, server = cls.servers_client.create_server(cls.name,
+                                                        cls.image_ref,
+                                                        cls.flavor_ref)
+        cls.server_id = server['id']
+
+        cls.servers_client.wait_for_server_status(cls.server_id, 'ACTIVE')
+
+    @classmethod
+    def tearDownClass(self, cls):
+        cls.servers_client.delete_server(cls.server_id)
+
+    @attr(type='positive')
+    def test_list_virtual_interfaces(self):
+        # Positive test:Should be able to GET the virtual interfaces list
+        # for a given server_id
+        resp, output = self.client.list_virtual_interfaces(self.server_id)
+        self.assertEqual(200, resp.status)
+        self.assertNotEqual(output, None)
+        virtual_interfaces = output
+        self.assertNotEqual(0, len(virtual_interfaces),
+                            'Expected virtual interfaces, got zero.')
+
+    @attr(type='negative')
+    def test_list_virtual_interfaces_invalid_server_id(self):
+        # Negative test: Should not be able to GET virtual interfaces
+        # for an invalid server_id
+        try:
+            resp, output = self.client.list_virtual_interfaces('!@#$%^&*()')
+        except exceptions.NotFound:
+            pass
+
+
+@attr(type='smoke')
+class VirtualInterfacesTestJSON(base.BaseComputeTestJSON,
+                                VirtualInterfacesTest):
+    @classmethod
+    def setUpClass(cls):
+        super(VirtualInterfacesTestJSON, cls).setUpClass()
+        VirtualInterfacesTest.setUpClass(cls)
+
+    @classmethod
+    def tearDownClass(cls):
+        VirtualInterfacesTest.tearDownClass(cls)
+        super(VirtualInterfacesTestJSON, cls).tearDownClass()
+
+
+@attr(type='smoke')
+class VirtualInterfacesTestXML(base.BaseComputeTestXML,
+                               VirtualInterfacesTest):
+    @classmethod
+    def setUpClass(cls):
+        super(VirtualInterfacesTestXML, cls).setUpClass()
+        VirtualInterfacesTest.setUpClass(cls)
+
+    @classmethod
+    def tearDownClass(cls):
+        VirtualInterfacesTest.tearDownClass(cls)
+        super(VirtualInterfacesTestXML, cls).tearDownClass()
diff --git a/tempest/tests/object_storage/test_object_expiry.py b/tempest/tests/object_storage/test_object_expiry.py
index 1738ddf..8411ef5 100644
--- a/tempest/tests/object_storage/test_object_expiry.py
+++ b/tempest/tests/object_storage/test_object_expiry.py
@@ -39,7 +39,8 @@
         """The test script fails in tear down class
         as the container contains expired objects (LP bug 1069849).
         But delete action for the expired object is raising
-        NotFound exception and also non empty container cannot be deleted."""
+        NotFound exception and also non empty container cannot be deleted.
+        """
 
         #Get list of all object in the container
         objlist = \
diff --git a/tools/install_venv.py b/tools/install_venv.py
index 52dbe39..20dcefa 100644
--- a/tools/install_venv.py
+++ b/tools/install_venv.py
@@ -38,6 +38,8 @@
 
 
 def print_help():
+    """This prints Help."""
+
     help = """
     Tempest development environment setup is complete.