port attach_interfaces and server_address tests into v3 part1

This changeset only copies the v2 files into the appropriate v3
directories unchanged. This is being tried in order to make
reviewing of the porting easier as gerrit will display only what
is actually changed for v3 rather than entirely new files.

Partially implements blueprint nova-v3-api-tests

Change-Id: I184874147cc872a42fb34f89f5ebe8bd763773b4
diff --git a/tempest/api/compute/v3/servers/test_attach_interfaces.py b/tempest/api/compute/v3/servers/test_attach_interfaces.py
new file mode 100644
index 0000000..a177cea
--- /dev/null
+++ b/tempest/api/compute/v3/servers/test_attach_interfaces.py
@@ -0,0 +1,118 @@
+# Copyright 2013 IBM Corp.
+# 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.api.compute import base
+from tempest.test import attr
+
+import time
+
+
+class AttachInterfacesTestJSON(base.BaseV2ComputeTest):
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        if not cls.config.service_available.neutron:
+            raise cls.skipException("Neutron is required")
+        super(AttachInterfacesTestJSON, cls).setUpClass()
+        cls.client = cls.os.interfaces_client
+
+    def _check_interface(self, iface, port_id=None, network_id=None,
+                         fixed_ip=None):
+        self.assertIn('port_state', iface)
+        if port_id:
+            self.assertEqual(iface['port_id'], port_id)
+        if network_id:
+            self.assertEqual(iface['net_id'], network_id)
+        if fixed_ip:
+            self.assertEqual(iface['fixed_ips'][0]['ip_address'], fixed_ip)
+
+    def _create_server_get_interfaces(self):
+        resp, server = self.create_test_server(wait_until='ACTIVE')
+        resp, ifs = self.client.list_interfaces(server['id'])
+        resp, body = self.client.wait_for_interface_status(
+            server['id'], ifs[0]['port_id'], 'ACTIVE')
+        ifs[0]['port_state'] = body['port_state']
+        return server, ifs
+
+    def _test_create_interface(self, server):
+        resp, iface = self.client.create_interface(server['id'])
+        resp, iface = self.client.wait_for_interface_status(
+            server['id'], iface['port_id'], 'ACTIVE')
+        self._check_interface(iface)
+        return iface
+
+    def _test_create_interface_by_network_id(self, server, ifs):
+        network_id = ifs[0]['net_id']
+        resp, iface = self.client.create_interface(server['id'],
+                                                   network_id=network_id)
+        resp, iface = self.client.wait_for_interface_status(
+            server['id'], iface['port_id'], 'ACTIVE')
+        self._check_interface(iface, network_id=network_id)
+        return iface
+
+    def _test_show_interface(self, server, ifs):
+        iface = ifs[0]
+        resp, _iface = self.client.show_interface(server['id'],
+                                                  iface['port_id'])
+        self.assertEqual(iface, _iface)
+
+    def _test_delete_interface(self, server, ifs):
+        # NOTE(danms): delete not the first or last, but one in the middle
+        iface = ifs[1]
+        self.client.delete_interface(server['id'], iface['port_id'])
+        for i in range(0, 5):
+            _r, _ifs = self.client.list_interfaces(server['id'])
+            if len(ifs) != len(_ifs):
+                break
+            time.sleep(1)
+
+        self.assertEqual(len(_ifs), len(ifs) - 1)
+        for _iface in _ifs:
+            self.assertNotEqual(iface['port_id'], _iface['port_id'])
+        return _ifs
+
+    def _compare_iface_list(self, list1, list2):
+        # NOTE(danms): port_state will likely have changed, so just
+        # confirm the port_ids are the same at least
+        list1 = [x['port_id'] for x in list1]
+        list2 = [x['port_id'] for x in list2]
+
+        self.assertEqual(sorted(list1), sorted(list2))
+
+    @attr(type='gate')
+    def test_create_list_show_delete_interfaces(self):
+        server, ifs = self._create_server_get_interfaces()
+        interface_count = len(ifs)
+        self.assertTrue(interface_count > 0)
+        self._check_interface(ifs[0])
+
+        iface = self._test_create_interface(server)
+        ifs.append(iface)
+
+        iface = self._test_create_interface_by_network_id(server, ifs)
+        ifs.append(iface)
+
+        resp, _ifs = self.client.list_interfaces(server['id'])
+        self._compare_iface_list(ifs, _ifs)
+
+        self._test_show_interface(server, ifs)
+
+        _ifs = self._test_delete_interface(server, ifs)
+        self.assertEqual(len(ifs) - 1, len(_ifs))
+
+
+class AttachInterfacesTestXML(AttachInterfacesTestJSON):
+    _interface = 'xml'
diff --git a/tempest/api/compute/v3/servers/test_server_addresses.py b/tempest/api/compute/v3/servers/test_server_addresses.py
new file mode 100644
index 0000000..7ca8a52
--- /dev/null
+++ b/tempest/api/compute/v3/servers/test_server_addresses.py
@@ -0,0 +1,84 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 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.api.compute import base
+from tempest import exceptions
+from tempest.test import attr
+
+
+class ServerAddressesTest(base.BaseV2ComputeTest):
+    _interface = 'json'
+
+    @classmethod
+    def setUpClass(cls):
+        super(ServerAddressesTest, cls).setUpClass()
+        cls.client = cls.servers_client
+
+        resp, cls.server = cls.create_test_server(wait_until='ACTIVE')
+
+    @attr(type=['negative', 'gate'])
+    def test_list_server_addresses_invalid_server_id(self):
+        # List addresses request should fail if server id not in system
+        self.assertRaises(exceptions.NotFound, self.client.list_addresses,
+                          '999')
+
+    @attr(type=['negative', 'gate'])
+    def test_list_server_addresses_by_network_neg(self):
+        # List addresses by network should fail if network name not valid
+        self.assertRaises(exceptions.NotFound,
+                          self.client.list_addresses_by_network,
+                          self.server['id'], 'invalid')
+
+    @attr(type='smoke')
+    def test_list_server_addresses(self):
+        # All public and private addresses for
+        # a server should be returned
+
+        resp, addresses = self.client.list_addresses(self.server['id'])
+        self.assertEqual('200', resp['status'])
+
+        # We do not know the exact network configuration, but an instance
+        # should at least have a single public or private address
+        self.assertTrue(len(addresses) >= 1)
+        for network_name, network_addresses in addresses.iteritems():
+            self.assertTrue(len(network_addresses) >= 1)
+            for address in network_addresses:
+                self.assertTrue(address['addr'])
+                self.assertTrue(address['version'])
+
+    @attr(type='smoke')
+    def test_list_server_addresses_by_network(self):
+        # Providing a network type should filter
+        # the addresses return by that type
+
+        resp, addresses = self.client.list_addresses(self.server['id'])
+
+        # Once again we don't know the environment's exact network config,
+        # but the response for each individual network should be the same
+        # as the partial result of the full address list
+        id = self.server['id']
+        for addr_type in addresses:
+            resp, addr = self.client.list_addresses_by_network(id, addr_type)
+            self.assertEqual('200', resp['status'])
+
+            addr = addr[addr_type]
+            for address in addresses[addr_type]:
+                self.assertTrue(any([a for a in addr if a == address]))
+
+
+class ServerAddressesTestXML(ServerAddressesTest):
+    _interface = 'xml'
diff --git a/tempest/services/compute/v3/json/interfaces_client.py b/tempest/services/compute/v3/json/interfaces_client.py
new file mode 100644
index 0000000..06e6476
--- /dev/null
+++ b/tempest/services/compute/v3/json/interfaces_client.py
@@ -0,0 +1,80 @@
+# Copyright 2013 IBM Corp.
+# 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 time
+
+from tempest.common.rest_client import RestClient
+from tempest import exceptions
+
+
+class InterfacesClientJSON(RestClient):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(InterfacesClientJSON, self).__init__(config, username, password,
+                                                   auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+
+    def list_interfaces(self, server):
+        resp, body = self.get('servers/%s/os-interface' % server)
+        body = json.loads(body)
+        return resp, body['interfaceAttachments']
+
+    def create_interface(self, server, port_id=None, network_id=None,
+                         fixed_ip=None):
+        post_body = dict(interfaceAttachment=dict())
+        if port_id:
+            post_body['port_id'] = port_id
+        if network_id:
+            post_body['net_id'] = network_id
+        if fixed_ip:
+            post_body['fixed_ips'] = [dict(ip_address=fixed_ip)]
+        post_body = json.dumps(post_body)
+        resp, body = self.post('servers/%s/os-interface' % server,
+                               headers=self.headers,
+                               body=post_body)
+        body = json.loads(body)
+        return resp, body['interfaceAttachment']
+
+    def show_interface(self, server, port_id):
+        resp, body = self.get('servers/%s/os-interface/%s' % (server, port_id))
+        body = json.loads(body)
+        return resp, body['interfaceAttachment']
+
+    def delete_interface(self, server, port_id):
+        resp, body = self.delete('servers/%s/os-interface/%s' % (server,
+                                                                 port_id))
+        return resp, body
+
+    def wait_for_interface_status(self, server, port_id, status):
+        """Waits for a interface to reach a given status."""
+        resp, body = self.show_interface(server, port_id)
+        interface_status = body['port_state']
+        start = int(time.time())
+
+        while(interface_status != status):
+            time.sleep(self.build_interval)
+            resp, body = self.show_interface(server, port_id)
+            interface_status = body['port_state']
+
+            timed_out = int(time.time()) - start >= self.build_timeout
+
+            if interface_status != status and timed_out:
+                message = ('Interface %s failed to reach %s status within '
+                           'the required time (%s s).' %
+                           (port_id, status, self.build_timeout))
+                raise exceptions.TimeoutException(message)
+
+        return resp, body
diff --git a/tempest/services/compute/v3/xml/interfaces_client.py b/tempest/services/compute/v3/xml/interfaces_client.py
new file mode 100644
index 0000000..a84e0bd
--- /dev/null
+++ b/tempest/services/compute/v3/xml/interfaces_client.py
@@ -0,0 +1,105 @@
+# Copyright 2013 IBM Corp.
+# 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 time
+
+from lxml import etree
+
+from tempest.common.rest_client import RestClientXML
+from tempest import exceptions
+from tempest.services.compute.xml.common import Document
+from tempest.services.compute.xml.common import Element
+from tempest.services.compute.xml.common import Text
+from tempest.services.compute.xml.common import xml_to_json
+
+
+class InterfacesClientXML(RestClientXML):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(InterfacesClientXML, self).__init__(config, username, password,
+                                                  auth_url, tenant_name)
+        self.service = self.config.compute.catalog_type
+
+    def _process_xml_interface(self, node):
+        iface = xml_to_json(node)
+        # NOTE(danms): if multiple addresses per interface is ever required,
+        # xml_to_json will need to be fixed or replaced in this case
+        iface['fixed_ips'] = [dict(iface['fixed_ips']['fixed_ip'].items())]
+        return iface
+
+    def list_interfaces(self, server):
+        resp, body = self.get('servers/%s/os-interface' % server, self.headers)
+        node = etree.fromstring(body)
+        interfaces = [self._process_xml_interface(x)
+                      for x in node.getchildren()]
+        return resp, interfaces
+
+    def create_interface(self, server, port_id=None, network_id=None,
+                         fixed_ip=None):
+        doc = Document()
+        iface = Element('interfaceAttachment')
+        if port_id:
+            _port_id = Element('port_id')
+            _port_id.append(Text(port_id))
+            iface.append(_port_id)
+        if network_id:
+            _network_id = Element('net_id')
+            _network_id.append(Text(network_id))
+            iface.append(_network_id)
+        if fixed_ip:
+            _fixed_ips = Element('fixed_ips')
+            _fixed_ip = Element('fixed_ip')
+            _ip_address = Element('ip_address')
+            _ip_address.append(Text(fixed_ip))
+            _fixed_ip.append(_ip_address)
+            _fixed_ips.append(_fixed_ip)
+            iface.append(_fixed_ips)
+        doc.append(iface)
+        resp, body = self.post('servers/%s/os-interface' % server,
+                               headers=self.headers,
+                               body=str(doc))
+        body = self._process_xml_interface(etree.fromstring(body))
+        return resp, body
+
+    def show_interface(self, server, port_id):
+        resp, body = self.get('servers/%s/os-interface/%s' % (server, port_id),
+                              self.headers)
+        body = self._process_xml_interface(etree.fromstring(body))
+        return resp, body
+
+    def delete_interface(self, server, port_id):
+        resp, body = self.delete('servers/%s/os-interface/%s' % (server,
+                                                                 port_id))
+        return resp, body
+
+    def wait_for_interface_status(self, server, port_id, status):
+        """Waits for a interface to reach a given status."""
+        resp, body = self.show_interface(server, port_id)
+        interface_status = body['port_state']
+        start = int(time.time())
+
+        while(interface_status != status):
+            time.sleep(self.build_interval)
+            resp, body = self.show_interface(server, port_id)
+            interface_status = body['port_state']
+
+            timed_out = int(time.time()) - start >= self.build_timeout
+
+            if interface_status != status and timed_out:
+                message = ('Interface %s failed to reach %s status within '
+                           'the required time (%s s).' %
+                           (port_id, status, self.build_timeout))
+                raise exceptions.TimeoutException(message)
+        return resp, body