Split cases for nova and neutron networks.
Added simlest network tests. Change-Id: Ifc3f951a29d85fad61869376bcbe602ae209f6c6
This commit is contained in:
parent
3ecaef77b7
commit
93aabba2ff
@ -60,9 +60,9 @@ now, 8-byte hashes are generated and returned for any ID to report.
|
|||||||
Solution: Nova GCE API just uses first key.
|
Solution: Nova GCE API just uses first key.
|
||||||
|
|
||||||
* Default Openstack flavors are available as machine types. GCE doesn't allow symbol '.' in machine type names,
|
* Default Openstack flavors are available as machine types. GCE doesn't allow symbol '.' in machine type names,
|
||||||
that's why GCE API plugin converts symbols '.' into '-' in 'get' requests (e.g. request of machine types converts
|
that's why GCE API plugin converts symbols '.' into '-' in 'get' requests (e.g. request of machine types converts
|
||||||
the name 'm1.tiny' into m1-tiny) and vise versa in 'put/post/delete' requests (e.g. instance creation converts
|
the name 'm1.tiny' into m1-tiny) and vise versa in 'put/post/delete' requests (e.g. instance creation converts
|
||||||
the name 'n1-standard-1' to 'n1.standard.1').
|
the name 'n1-standard-1' to 'n1.standard.1').
|
||||||
|
|
||||||
Authentication specifics
|
Authentication specifics
|
||||||
========================
|
========================
|
||||||
|
@ -207,6 +207,14 @@ class API(object):
|
|||||||
self._delete_db_item(context, item)
|
self._delete_db_item(context, item)
|
||||||
return only_os_items
|
return only_os_items
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _from_gce(name):
|
||||||
|
return name.replace("-", ".")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _to_gce(name):
|
||||||
|
return name.replace(".", "-")
|
||||||
|
|
||||||
|
|
||||||
class _CallbackReasons(object):
|
class _CallbackReasons(object):
|
||||||
check_delete = 1
|
check_delete = 1
|
||||||
|
@ -119,7 +119,7 @@ def keystone(context):
|
|||||||
# Ver2 doesn't create session and performs
|
# Ver2 doesn't create session and performs
|
||||||
# authentication automatically, but Ver3 does create session
|
# authentication automatically, but Ver3 does create session
|
||||||
# if it's not provided and doesn't perform authentication.
|
# if it's not provided and doesn't perform authentication.
|
||||||
# TODO(use sessions)
|
# TODO(alexey-mr): use sessions
|
||||||
c.authenticate()
|
c.authenticate()
|
||||||
return c
|
return c
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ class Controller(object):
|
|||||||
# Ver2 doesn't create session and performs
|
# Ver2 doesn't create session and performs
|
||||||
# authentication automatically, but Ver3 does create session
|
# authentication automatically, but Ver3 does create session
|
||||||
# if it's not provided and doesn't perform authentication.
|
# if it's not provided and doesn't perform authentication.
|
||||||
# TODO(use sessions)
|
# TODO(alexey-mr): use sessions
|
||||||
keystone.authenticate()
|
keystone.authenticate()
|
||||||
catalog = keystone.service_catalog.get_data()
|
catalog = keystone.service_catalog.get_data()
|
||||||
public_url = clients.get_url_from_catalog(catalog, "gceapi")
|
public_url = clients.get_url_from_catalog(catalog, "gceapi")
|
||||||
|
@ -60,9 +60,3 @@ class API(base_api.API):
|
|||||||
def _prepare_item(self, item):
|
def _prepare_item(self, item):
|
||||||
item["name"] = self._to_gce(item["name"])
|
item["name"] = self._to_gce(item["name"])
|
||||||
return item
|
return item
|
||||||
|
|
||||||
def _from_gce(self, name):
|
|
||||||
return name.replace("-", ".")
|
|
||||||
|
|
||||||
def _to_gce(self, name):
|
|
||||||
return name.replace(".", "-")
|
|
||||||
|
@ -154,7 +154,7 @@ class Controller(object):
|
|||||||
# Ver2 doesn't create session and performs
|
# Ver2 doesn't create session and performs
|
||||||
# authentication automatically, but Ver3 does create session
|
# authentication automatically, but Ver3 does create session
|
||||||
# if it's not provided and doesn't perform authentication.
|
# if it's not provided and doesn't perform authentication.
|
||||||
# TODO(use sessions)
|
# TODO(alexy-mr): use sessions
|
||||||
keystone.authenticate()
|
keystone.authenticate()
|
||||||
client.auth_token = keystone.auth_token
|
client.auth_token = keystone.auth_token
|
||||||
s = keystone.auth_ref.issued
|
s = keystone.auth_ref.issued
|
||||||
@ -223,7 +223,7 @@ class AuthProtocol(object):
|
|||||||
# Ver2 doesn't create session and performs
|
# Ver2 doesn't create session and performs
|
||||||
# authentication automatically, but Ver3 does create session
|
# authentication automatically, but Ver3 does create session
|
||||||
# if it's not provided and doesn't perform authentication.
|
# if it's not provided and doesn't perform authentication.
|
||||||
# TODO(use sessions)
|
# TODO(alexey-mr): use sessions
|
||||||
keystone.authenticate()
|
keystone.authenticate()
|
||||||
scoped_token = keystone.auth_token
|
scoped_token = keystone.auth_token
|
||||||
env["HTTP_X_AUTH_TOKEN"] = scoped_token
|
env["HTTP_X_AUTH_TOKEN"] = scoped_token
|
||||||
|
@ -20,6 +20,19 @@ from gceapi import exception
|
|||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
# OS usual region names are in PascalCase - e.g. RegionOne,
|
||||||
|
# GCE region name should matche the regexp [a-z](?:[-a-z0-9]{0,61}[a-z0-9])?
|
||||||
|
_OS_GCE_MAP = {
|
||||||
|
'RegionOne': 'region-one',
|
||||||
|
'RegionTwo': 'region-two',
|
||||||
|
'RegionThree': 'region-three',
|
||||||
|
'RegionFour': 'region-four',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _map_region_name(name):
|
||||||
|
return _OS_GCE_MAP.get(name, name)
|
||||||
|
|
||||||
|
|
||||||
class API(base_api.API):
|
class API(base_api.API):
|
||||||
"""GCE Regions API
|
"""GCE Regions API
|
||||||
@ -33,7 +46,7 @@ class API(base_api.API):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(API, self).__init__(*args, **kwargs)
|
super(API, self).__init__(*args, **kwargs)
|
||||||
self._REGIONS = [CONF.get("region").strip()]
|
self._REGIONS = [_map_region_name(CONF.get("region").strip())]
|
||||||
|
|
||||||
def _get_type(self):
|
def _get_type(self):
|
||||||
return self.KIND
|
return self.KIND
|
||||||
|
@ -58,11 +58,13 @@ if [[ ! -f $TEST_CONFIG_DIR/$TEST_CONFIG ]]; then
|
|||||||
[[ "$?" -eq 0 ]] || { echo "Failed to prepare flavor"; exit 1; }
|
[[ "$?" -eq 0 ]] || { echo "Failed to prepare flavor"; exit 1; }
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# create network
|
# create default network
|
||||||
if [[ -n $(openstack service list | grep neutron) ]]; then
|
if [[ -n $(openstack service list | grep neutron) ]]; then
|
||||||
net_id=$(neutron net-create --tenant-id $project_id "private" | grep ' id ' | awk '{print $4}')
|
# neutron networking
|
||||||
|
networking="neutron"
|
||||||
|
net_id=$(neutron net-create --tenant-id $project_id "default" | grep ' id ' | awk '{print $4}')
|
||||||
[[ -n "$net_id" ]] || { echo "net-create failed"; exit 1; }
|
[[ -n "$net_id" ]] || { echo "net-create failed"; exit 1; }
|
||||||
subnet_id=$(neutron subnet-create --tenant-id $project_id --ip_version 4 --gateway 10.0.0.1 --name "private_subnet" $net_id 10.0.0.0/24 | grep ' id ' | awk '{print $4}')
|
subnet_id=$(neutron subnet-create --tenant-id $project_id --ip_version 4 --gateway 10.240.0.1 --name "private_subnet" $net_id 10.240.0.0/24 | grep ' id ' | awk '{print $4}')
|
||||||
[[ -n "$subnet_id" ]] || { echo "subnet-create failed"; exit 1; }
|
[[ -n "$subnet_id" ]] || { echo "subnet-create failed"; exit 1; }
|
||||||
router_id=$(neutron router-create --tenant-id $project_id "private_router" | grep ' id ' | awk '{print $4}')
|
router_id=$(neutron router-create --tenant-id $project_id "private_router" | grep ' id ' | awk '{print $4}')
|
||||||
[[ -n "$router_id" ]] || { echo "router-create failed"; exit 1; }
|
[[ -n "$router_id" ]] || { echo "router-create failed"; exit 1; }
|
||||||
@ -72,6 +74,10 @@ if [[ ! -f $TEST_CONFIG_DIR/$TEST_CONFIG ]]; then
|
|||||||
[[ -n "$public_net_id" ]] || { echo "can't find public network"; exit 1; }
|
[[ -n "$public_net_id" ]] || { echo "can't find public network"; exit 1; }
|
||||||
neutron router-gateway-set $router_id $public_net_id
|
neutron router-gateway-set $router_id $public_net_id
|
||||||
[[ "$?" -eq 0 ]] || { echo "router-gateway-set failed"; exit 1; }
|
[[ "$?" -eq 0 ]] || { echo "router-gateway-set failed"; exit 1; }
|
||||||
|
else
|
||||||
|
# nova networking
|
||||||
|
networking="nova-network"
|
||||||
|
nova network-create "default" --fixed-range-v4 10.240.0.0/24 --gateway 10.240.0.1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#create image in raw format
|
#create image in raw format
|
||||||
@ -117,7 +123,8 @@ discovery_url=${GCE_DISCOVERY_URL:-'/discovery/v1/apis/{api}/{apiVersion}/rest'}
|
|||||||
# GCE resource IDs for testing
|
# GCE resource IDs for testing
|
||||||
project_id=${OS_PROJECT_NAME}
|
project_id=${OS_PROJECT_NAME}
|
||||||
zone=${ZONE:-'nova'}
|
zone=${ZONE:-'nova'}
|
||||||
region=${REGION:-'RegionOne'}
|
networking=${networking}
|
||||||
|
region=${REGION:-'region-one'}
|
||||||
# convert flavor name: becase GCE dowsn't allows '.' and converts '-' into '.'
|
# convert flavor name: becase GCE dowsn't allows '.' and converts '-' into '.'
|
||||||
machine_type=${flavor_name//\./-}
|
machine_type=${flavor_name//\./-}
|
||||||
image=${os_image_name}
|
image=${os_image_name}
|
||||||
|
129
gceapi/tests/functional/api/test_addresses.py
Normal file
129
gceapi/tests/functional/api/test_addresses.py
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
# Copyright 2015 United States Government as represented by the
|
||||||
|
# Administrator of the National Aeronautics and Space Administration.
|
||||||
|
# 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 gceapi.tests.functional import test_base
|
||||||
|
|
||||||
|
|
||||||
|
CREATE_ADDRESS_TEMPLATE = {
|
||||||
|
"name": "${name}",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _prepare_address_create_parameters(**kwargs):
|
||||||
|
return test_base.insert_json_parameters(CREATE_ADDRESS_TEMPLATE, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class TestAddressesBase(test_base.GCETestCase):
|
||||||
|
@property
|
||||||
|
def addresses(self):
|
||||||
|
res = self.api.compute.addresses()
|
||||||
|
self.assertIsNotNone(
|
||||||
|
res,
|
||||||
|
'Null addresses object, api is not built properly')
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _create_address(self, options):
|
||||||
|
self._add_cleanup(self._delete_address, options['name'])
|
||||||
|
cfg = self.cfg
|
||||||
|
project_id = cfg.project_id
|
||||||
|
region = cfg.region
|
||||||
|
config = _prepare_address_create_parameters(**options)
|
||||||
|
self.trace('Crete address with options {}'.format(config))
|
||||||
|
request = self.addresses.insert(
|
||||||
|
project=project_id,
|
||||||
|
region=region,
|
||||||
|
body=config)
|
||||||
|
self._execute_async_request(request, project_id, region=region)
|
||||||
|
|
||||||
|
def _delete_address(self, name):
|
||||||
|
cfg = self.cfg
|
||||||
|
project_id = cfg.project_id
|
||||||
|
region = cfg.region
|
||||||
|
self.trace('Delete address: project_id={} region={} name={}'.
|
||||||
|
format(project_id, region, name))
|
||||||
|
request = self.addresses.delete(
|
||||||
|
project=project_id,
|
||||||
|
region=region,
|
||||||
|
address=name)
|
||||||
|
self._remove_cleanup(self._delete_address, name)
|
||||||
|
self._execute_async_request(request, project_id, region=region)
|
||||||
|
|
||||||
|
def _list_addresses(self):
|
||||||
|
cfg = self.cfg
|
||||||
|
project_id = cfg.project_id
|
||||||
|
region = cfg.region
|
||||||
|
self.trace('List addresses: project_id={} region={}'.
|
||||||
|
format(project_id, region))
|
||||||
|
request = self.addresses.list(
|
||||||
|
project=project_id,
|
||||||
|
region=region)
|
||||||
|
result = request.execute()
|
||||||
|
self.trace('Addresses: {}'.format(result))
|
||||||
|
self.api.validate_schema(value=result, schema_name='AddressList')
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _get_address(self, name):
|
||||||
|
cfg = self.cfg
|
||||||
|
project_id = cfg.project_id
|
||||||
|
region = cfg.region
|
||||||
|
self.trace('Get address: project_id={} region={} name={}'.
|
||||||
|
format(project_id, region, name))
|
||||||
|
request = self.addresses.get(
|
||||||
|
project=project_id,
|
||||||
|
region=region,
|
||||||
|
address=name)
|
||||||
|
result = request.execute()
|
||||||
|
self.trace('Addresses: {}'.format(result))
|
||||||
|
self.api.validate_schema(value=result, schema_name='Address')
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class TestAddressesCRUD(TestAddressesBase):
|
||||||
|
@property
|
||||||
|
def addresses(self):
|
||||||
|
res = self.api.compute.addresses()
|
||||||
|
self.assertIsNotNone(
|
||||||
|
res,
|
||||||
|
'Null addresses object, api is not built properly')
|
||||||
|
return res
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestAddressesCRUD, self).setUp()
|
||||||
|
self._address_name = self._rand_name('testaddr')
|
||||||
|
|
||||||
|
def _create(self):
|
||||||
|
options = {
|
||||||
|
'name': self._address_name
|
||||||
|
}
|
||||||
|
self._create_address(options)
|
||||||
|
|
||||||
|
def _read(self):
|
||||||
|
result = self._get_address(self._address_name)
|
||||||
|
self.assertEqual(self._address_name, result['name'])
|
||||||
|
result = self._list_addresses()
|
||||||
|
self.assertFind(self._address_name, result)
|
||||||
|
|
||||||
|
def _update(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _delete(self):
|
||||||
|
self._delete_address(self._address_name)
|
||||||
|
|
||||||
|
def test_crud(self):
|
||||||
|
self._create()
|
||||||
|
self._read()
|
||||||
|
self._update()
|
||||||
|
self._delete()
|
@ -15,17 +15,11 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
from string import Template
|
|
||||||
|
|
||||||
from json import dumps
|
|
||||||
from json import loads
|
|
||||||
|
|
||||||
from gceapi.tests.functional import test_base
|
from gceapi.tests.functional import test_base
|
||||||
|
|
||||||
|
|
||||||
BASE_COMPUTE_URL = '{address}/compute/v1'
|
|
||||||
CREATE_INSTANCE_TEMPLATE = {
|
CREATE_INSTANCE_TEMPLATE = {
|
||||||
"name": "${instance}",
|
"name": "${name}",
|
||||||
"description": "Testing instance",
|
"description": "Testing instance",
|
||||||
"machineType": "zones/${zone}/machineTypes/${machine_type}",
|
"machineType": "zones/${zone}/machineTypes/${machine_type}",
|
||||||
"disks": [
|
"disks": [
|
||||||
@ -66,30 +60,13 @@ CREATE_INSTANCE_TEMPLATE = {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
CREATE_NETWORK_TEMPLATE = {
|
|
||||||
"name": "${name}",
|
|
||||||
"IPv4Range": "10.240.0.0/16",
|
|
||||||
"description": "testing network ${name}",
|
|
||||||
"gatewayIPv4": "10.240.0.1"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _insert_json_parameters(obj, **kwargs):
|
def _prepare_instance_insert_parameters(**kwargs):
|
||||||
s = dumps(obj)
|
return test_base.insert_json_parameters(CREATE_INSTANCE_TEMPLATE, **kwargs)
|
||||||
t = Template(s)
|
|
||||||
s = t.substitute(**kwargs)
|
|
||||||
return loads(s)
|
|
||||||
|
|
||||||
|
|
||||||
def _prepare_instace_insert_parameters(**kwargs):
|
class TestInstancesBase(test_base.GCETestCase):
|
||||||
return _insert_json_parameters(CREATE_INSTANCE_TEMPLATE, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def _prepare_network_create_parameters(**kwargs):
|
|
||||||
return _insert_json_parameters(CREATE_NETWORK_TEMPLATE, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class TestIntancesBase(test_base.GCETestCase):
|
|
||||||
@property
|
@property
|
||||||
def instances(self):
|
def instances(self):
|
||||||
res = self.api.compute.instances()
|
res = self.api.compute.instances()
|
||||||
@ -98,132 +75,87 @@ class TestIntancesBase(test_base.GCETestCase):
|
|||||||
'Null instances object, api is not built properly')
|
'Null instances object, api is not built properly')
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@property
|
def _create_instance(self, options):
|
||||||
def networks(self):
|
self._add_cleanup(self._delete_instance, options['name'])
|
||||||
res = self.api.compute.networks()
|
|
||||||
self.assertIsNotNone(
|
|
||||||
res,
|
|
||||||
'Null networks object, api is not built properly')
|
|
||||||
return res
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestIntancesBase, self).setUp()
|
|
||||||
self._instance_name = self.getUniqueString('testinst')
|
|
||||||
self._network_name = self.getUniqueString('testnet')
|
|
||||||
|
|
||||||
def _create_network(self):
|
|
||||||
cfg = self.cfg
|
|
||||||
project_id = cfg.project_id
|
|
||||||
network = self._network_name
|
|
||||||
kw = {
|
|
||||||
'name': network,
|
|
||||||
}
|
|
||||||
config = _prepare_network_create_parameters(**kw)
|
|
||||||
self.trace('Crete network with options {}'.format(config))
|
|
||||||
request = self.networks.insert(
|
|
||||||
project=project_id,
|
|
||||||
body=config)
|
|
||||||
result = self._execute_async_request(request, project_id)
|
|
||||||
self.api.validate_schema(value=result, schema_name='Operation')
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _delete_network(self):
|
|
||||||
cfg = self.cfg
|
|
||||||
project_id = cfg.project_id
|
|
||||||
network = self._network_name
|
|
||||||
self.trace(
|
|
||||||
'Delete network: project_id={} network={}'.
|
|
||||||
format(project_id, network))
|
|
||||||
request = self.networks.delete(
|
|
||||||
project=project_id,
|
|
||||||
network=network)
|
|
||||||
result = self._execute_async_request(request, project_id)
|
|
||||||
self.api.validate_schema(value=result, schema_name='Operation')
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _create_instance(self):
|
|
||||||
cfg = self.cfg
|
cfg = self.cfg
|
||||||
project_id = cfg.project_id
|
project_id = cfg.project_id
|
||||||
zone = cfg.zone
|
zone = cfg.zone
|
||||||
kw = {
|
config = _prepare_instance_insert_parameters(**options)
|
||||||
'zone': zone,
|
|
||||||
'instance': self._instance_name,
|
|
||||||
'machine_type': cfg.machine_type,
|
|
||||||
'image': cfg.image,
|
|
||||||
'network': self._network_name,
|
|
||||||
}
|
|
||||||
config = _prepare_instace_insert_parameters(**kw)
|
|
||||||
self.trace('Crete instance with options {}'.format(config))
|
self.trace('Crete instance with options {}'.format(config))
|
||||||
request = self.instances.insert(
|
request = self.instances.insert(
|
||||||
project=project_id,
|
project=project_id,
|
||||||
zone=zone,
|
zone=zone,
|
||||||
body=config)
|
body=config)
|
||||||
result = self._execute_async_request(request, project_id, zone=zone)
|
self._execute_async_request(request, project_id, zone=zone)
|
||||||
self.api.validate_schema(value=result, schema_name='Operation')
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _delete_instance(self):
|
def _delete_instance(self, name):
|
||||||
cfg = self.cfg
|
cfg = self.cfg
|
||||||
project_id = cfg.project_id
|
project_id = cfg.project_id
|
||||||
zone = cfg.zone
|
zone = cfg.zone
|
||||||
instance = self._instance_name
|
self.trace('Delete instance: project_id={} zone={} instance {}'.
|
||||||
self.trace(
|
format(project_id, zone, name))
|
||||||
'Delete instance: project_id={} zone={} instance {}'.
|
|
||||||
format(project_id, zone, instance))
|
|
||||||
request = self.instances.delete(
|
request = self.instances.delete(
|
||||||
project=project_id,
|
project=project_id,
|
||||||
zone=zone,
|
zone=zone,
|
||||||
instance=instance)
|
instance=name)
|
||||||
result = self._execute_async_request(request, project_id, zone=zone)
|
self._remove_cleanup(self._delete_instance, name)
|
||||||
self.api.validate_schema(value=result, schema_name='Operation')
|
self._execute_async_request(request, project_id, zone=zone)
|
||||||
return result
|
|
||||||
|
|
||||||
def _list(self):
|
def _list_instances(self):
|
||||||
project_id = self.cfg.project_id
|
project_id = self.cfg.project_id
|
||||||
zone = self.cfg.zone
|
zone = self.cfg.zone
|
||||||
self.trace(
|
self.trace('List instances: project_id={} zone={}'.
|
||||||
'List instances: project_id={} zone={}'.format(project_id, zone))
|
format(project_id, zone))
|
||||||
request = self.instances.list(project=project_id, zone=zone)
|
request = self.instances.list(project=project_id, zone=zone)
|
||||||
self._trace_request(request)
|
self.trace_request(request)
|
||||||
result = request.execute()
|
result = request.execute()
|
||||||
self.trace('Instances: {}'.format(result))
|
self.trace('Instances: {}'.format(result))
|
||||||
self.api.validate_schema(value=result, schema_name='InstanceList')
|
self.api.validate_schema(value=result, schema_name='InstanceList')
|
||||||
self.assertFind(self._instance_name, result)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _get(self):
|
def _get_instance(self, name):
|
||||||
project_id = self.cfg.project_id
|
project_id = self.cfg.project_id
|
||||||
zone = self.cfg.zone
|
zone = self.cfg.zone
|
||||||
instance = self._instance_name
|
self.trace('Get instance: project_id={} zone={} instance={}'.
|
||||||
self.trace(
|
format(project_id, zone, name))
|
||||||
'Get instance: project_id={} zone={} instance={}'.
|
|
||||||
format(project_id, zone, instance))
|
|
||||||
request = self.instances.get(
|
request = self.instances.get(
|
||||||
project=project_id,
|
project=project_id,
|
||||||
zone=zone,
|
zone=zone,
|
||||||
instance=instance)
|
instance=name)
|
||||||
result = request.execute()
|
result = request.execute()
|
||||||
self.trace('Instance: {}'.format(result))
|
self.trace('Instance: {}'.format(result))
|
||||||
self.api.validate_schema(value=result, schema_name='Instance')
|
self.api.validate_schema(value=result, schema_name='Instance')
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class TestIntancesCRUD(TestIntancesBase):
|
class TestInstancesCRUD(TestInstancesBase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestInstancesCRUD, self).setUp()
|
||||||
|
self._instance_name = self._rand_name('testinst')
|
||||||
|
|
||||||
def _create(self):
|
def _create(self):
|
||||||
self._create_network()
|
cfg = self.cfg
|
||||||
self._create_instance()
|
options = {
|
||||||
|
'zone': cfg.zone,
|
||||||
|
'name': self._instance_name,
|
||||||
|
'machine_type': cfg.machine_type,
|
||||||
|
'image': cfg.image,
|
||||||
|
'network': 'default',
|
||||||
|
}
|
||||||
|
self._create_instance(options)
|
||||||
|
|
||||||
def _read(self):
|
def _read(self):
|
||||||
self._get()
|
result = self._get_instance(self._instance_name)
|
||||||
self._list()
|
self.assertEqual(self._instance_name, result['name'])
|
||||||
|
result = self._list_instances()
|
||||||
|
self.assertFind(self._instance_name, result)
|
||||||
|
|
||||||
def _update(self):
|
def _update(self):
|
||||||
#TODO(to impl simple update cases)
|
# TODO(alexey-mr): to impl simple update cases
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _delete(self):
|
def _delete(self):
|
||||||
self._delete_instance()
|
self._delete_instance(self._instance_name)
|
||||||
self._delete_network()
|
|
||||||
|
|
||||||
def test_crud(self):
|
def test_crud(self):
|
||||||
self._create()
|
self._create()
|
||||||
|
132
gceapi/tests/functional/api/test_networks.py
Normal file
132
gceapi/tests/functional/api/test_networks.py
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
# Copyright 2015 United States Government as represented by the
|
||||||
|
# Administrator of the National Aeronautics and Space Administration.
|
||||||
|
# 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 gceapi.tests.functional import test_base
|
||||||
|
|
||||||
|
|
||||||
|
CREATE_NETWORK_TEMPLATE = {
|
||||||
|
"name": "${name}",
|
||||||
|
"IPv4Range": "${ip_range}",
|
||||||
|
"description": "testing network ${name}",
|
||||||
|
"gatewayIPv4": "${gateway}"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _prepare_network_create_parameters(**kwargs):
|
||||||
|
return test_base.insert_json_parameters(CREATE_NETWORK_TEMPLATE, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class TestNetworksBase(test_base.GCETestCase):
|
||||||
|
@property
|
||||||
|
def networks(self):
|
||||||
|
res = self.api.compute.networks()
|
||||||
|
self.assertIsNotNone(
|
||||||
|
res,
|
||||||
|
'Null networks object, api is not built properly')
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _create_network(self, options):
|
||||||
|
self._add_cleanup(self._delete_network, options['name'])
|
||||||
|
project_id = self.cfg.project_id
|
||||||
|
config = _prepare_network_create_parameters(**options)
|
||||||
|
self.trace('Crete network with options {}'.format(config))
|
||||||
|
request = self.networks.insert(
|
||||||
|
project=project_id,
|
||||||
|
body=config)
|
||||||
|
self._execute_async_request(request, project_id)
|
||||||
|
|
||||||
|
def _delete_network(self, name):
|
||||||
|
cfg = self.cfg
|
||||||
|
project_id = cfg.project_id
|
||||||
|
self.trace('Delete network: project_id={} network={}'.
|
||||||
|
format(project_id, name))
|
||||||
|
request = self.networks.delete(
|
||||||
|
project=project_id,
|
||||||
|
network=name)
|
||||||
|
self._remove_cleanup(self._delete_network, name)
|
||||||
|
self._execute_async_request(request, project_id)
|
||||||
|
|
||||||
|
def _list_networks(self):
|
||||||
|
project_id = self.cfg.project_id
|
||||||
|
self.trace('List networks: project_id={}'.format(project_id))
|
||||||
|
request = self.networks.list(project=project_id)
|
||||||
|
self.trace_request(request)
|
||||||
|
result = request.execute()
|
||||||
|
self.trace('Networks: {}'.format(result))
|
||||||
|
self.api.validate_schema(value=result, schema_name='NetworkList')
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _get_network(self, name):
|
||||||
|
project_id = self.cfg.project_id
|
||||||
|
self.trace('Get network: project_id={} network={}'.
|
||||||
|
format(project_id, name))
|
||||||
|
request = self.networks.get(
|
||||||
|
project=project_id,
|
||||||
|
network=name)
|
||||||
|
result = request.execute()
|
||||||
|
self.trace('Network: {}'.format(result))
|
||||||
|
self.api.validate_schema(value=result, schema_name='Network')
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class TestReadDefaultNetwork(TestNetworksBase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestReadDefaultNetwork, self).setUp()
|
||||||
|
self._network_name = 'default'
|
||||||
|
|
||||||
|
def test_get(self):
|
||||||
|
self._get_network(self._network_name)
|
||||||
|
|
||||||
|
def test_list(self):
|
||||||
|
result = self._list_networks()
|
||||||
|
self.assertFind(self._network_name, result)
|
||||||
|
|
||||||
|
|
||||||
|
class TestNetworksCRUD(TestNetworksBase):
|
||||||
|
def setUp(self):
|
||||||
|
if self.cfg.networking == 'nova-network':
|
||||||
|
self.skipTest('Skip network because of nova-network')
|
||||||
|
return
|
||||||
|
super(TestNetworksCRUD, self).setUp()
|
||||||
|
self._network_name = self._rand_name('network')
|
||||||
|
|
||||||
|
def _create(self):
|
||||||
|
options = {
|
||||||
|
'name': self._network_name,
|
||||||
|
'ip_range': '10.240.0.0/16',
|
||||||
|
'gateway': '10.240.0.1'
|
||||||
|
}
|
||||||
|
# TODO(alexey-mr): gateway is optional, so add case with absent one
|
||||||
|
self._create_network(options)
|
||||||
|
|
||||||
|
def _read(self):
|
||||||
|
result = self._get_network(self._network_name)
|
||||||
|
self.assertEqual(self._network_name, result['name'])
|
||||||
|
result = self._list_networks()
|
||||||
|
self.assertFind(self._network_name, result)
|
||||||
|
|
||||||
|
def _update(self):
|
||||||
|
# TODO(alexey-mr): to be implemented
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _delete(self):
|
||||||
|
self._delete_network(self._network_name)
|
||||||
|
|
||||||
|
def test_crud(self):
|
||||||
|
self._create()
|
||||||
|
self._read()
|
||||||
|
self._update()
|
||||||
|
self._delete()
|
@ -14,8 +14,6 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from gceapi.tests.functional import test_base
|
from gceapi.tests.functional import test_base
|
||||||
|
|
||||||
|
|
||||||
@ -34,8 +32,6 @@ class TestRegions(test_base.GCETestCase):
|
|||||||
'Null regions object, api is not built properly')
|
'Null regions object, api is not built properly')
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# TODO(alexey-mr): Google allows [a-z](?:[-a-z0-9]{0,61}[a-z0-9])?
|
|
||||||
@unittest.skip("Skip test for now: google dosnt't allow name RegionOne")
|
|
||||||
def test_describe(self):
|
def test_describe(self):
|
||||||
project_id = self.cfg.project_id
|
project_id = self.cfg.project_id
|
||||||
region = self.cfg.region
|
region = self.cfg.region
|
||||||
|
@ -31,13 +31,14 @@ import tempest.test
|
|||||||
|
|
||||||
CONF = config.CONF
|
CONF = config.CONF
|
||||||
LOG = logging.getLogger("tempest.thirdparty.gce")
|
LOG = logging.getLogger("tempest.thirdparty.gce")
|
||||||
|
REGION_NAME = 'region-one'
|
||||||
|
|
||||||
|
|
||||||
class GCEConnection(rest_client.RestClient):
|
class GCEConnection(rest_client.RestClient):
|
||||||
|
|
||||||
def __init__(self, auth_provider):
|
def __init__(self, auth_provider):
|
||||||
super(GCEConnection, self).__init__(auth_provider,
|
super(GCEConnection, self).__init__(auth_provider,
|
||||||
"gceapi", "RegionOne")
|
"gceapi", REGION_NAME)
|
||||||
self.service = CONF.gceapi.catalog_type
|
self.service = CONF.gceapi.catalog_type
|
||||||
|
|
||||||
def set_zone(self, zone):
|
def set_zone(self, zone):
|
||||||
|
@ -79,8 +79,11 @@ OPTIONS = [
|
|||||||
default='nova',
|
default='nova',
|
||||||
help='GCE Zone for testing'),
|
help='GCE Zone for testing'),
|
||||||
cfg.StrOpt('region',
|
cfg.StrOpt('region',
|
||||||
default='RegionOne',
|
default='us-central1',
|
||||||
help='GCE Region for testing'),
|
help='GCE Region for testing'),
|
||||||
|
cfg.StrOpt('networking',
|
||||||
|
default='neutron',
|
||||||
|
help='Types of OS networking: neutron or nova-network'),
|
||||||
|
|
||||||
cfg.StrOpt('machine_type',
|
cfg.StrOpt('machine_type',
|
||||||
default='n1-standard-1',
|
default='n1-standard-1',
|
||||||
|
@ -15,39 +15,34 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
from keystoneclient.client import Client as KeystoneClient
|
from keystoneclient import client as keystone_client
|
||||||
from oauth2client.client import AccessTokenCredentials
|
from oauth2client import client as oauth_client
|
||||||
from oauth2client.client import GoogleCredentials
|
|
||||||
|
|
||||||
|
|
||||||
class CredentialsProvider(object):
|
class CredentialsProvider(object):
|
||||||
def __init__(self, supp):
|
def __init__(self, cfg):
|
||||||
self._supp = supp
|
self.cfg = cfg
|
||||||
|
|
||||||
def _trace(self, msg):
|
@staticmethod
|
||||||
self._supp.trace(msg)
|
def _get_app_credentials():
|
||||||
|
return oauth_client.GoogleCredentials.get_application_default()
|
||||||
|
|
||||||
def _get_app_credentials(self):
|
def _get_token_credentials(self):
|
||||||
self._trace('Create GoogleCredentials from default app file')
|
|
||||||
return GoogleCredentials.get_application_default()
|
|
||||||
|
|
||||||
def _get_token_crenetials(self):
|
|
||||||
client = self._create_keystone_client()
|
client = self._create_keystone_client()
|
||||||
token = client.auth_token
|
token = client.auth_token
|
||||||
self._trace('Created token {}'.format(token))
|
return oauth_client.AccessTokenCredentials(
|
||||||
return AccessTokenCredentials(access_token=token,
|
access_token=token,
|
||||||
user_agent='GCE test')
|
user_agent='GCE test')
|
||||||
|
|
||||||
def _create_keystone_client(self):
|
def _create_keystone_client(self):
|
||||||
cfg = self._supp.cfg
|
cfg = self.cfg
|
||||||
auth_data = {
|
auth_data = {
|
||||||
'username': cfg.username,
|
'username': cfg.username,
|
||||||
'password': cfg.password,
|
'password': cfg.password,
|
||||||
'tenant_name': cfg.project_id,
|
'tenant_name': cfg.project_id,
|
||||||
'auth_url': cfg.auth_url
|
'auth_url': cfg.auth_url
|
||||||
}
|
}
|
||||||
self._trace('Create keystone client, auth_data={}'.format(auth_data))
|
client = keystone_client.Client(**auth_data)
|
||||||
client = KeystoneClient(**auth_data)
|
|
||||||
if not client.authenticate():
|
if not client.authenticate():
|
||||||
raise Exception('Failed to authenticate user')
|
raise Exception('Failed to authenticate user')
|
||||||
return client
|
return client
|
||||||
@ -58,9 +53,9 @@ class CredentialsProvider(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def credentials(self):
|
def credentials(self):
|
||||||
cred_type = self._supp.cfg.cred_type
|
cred_type = self.cfg.cred_type
|
||||||
if cred_type == 'os_token':
|
if cred_type == 'os_token':
|
||||||
return self._get_token_crenetials()
|
return self._get_token_credentials()
|
||||||
elif cred_type == 'gcloud_auth':
|
elif cred_type == 'gcloud_auth':
|
||||||
return self._get_app_credentials()
|
return self._get_app_credentials()
|
||||||
else:
|
else:
|
||||||
|
@ -14,33 +14,43 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import json
|
||||||
|
import string
|
||||||
import time
|
import time
|
||||||
|
import traceback
|
||||||
|
|
||||||
from googleapiclient.discovery import build
|
from googleapiclient import discovery
|
||||||
from googleapiclient.schema import Schemas
|
from googleapiclient import schema
|
||||||
from jsonschema import RefResolver
|
import jsonschema
|
||||||
from jsonschema import validate
|
from oslo_log import log as logging
|
||||||
from tempest_lib import base
|
from tempest_lib import base
|
||||||
|
from tempest_lib.common.utils import data_utils
|
||||||
|
|
||||||
from gceapi.tests.functional import config
|
from gceapi.tests.functional import config
|
||||||
from gceapi.tests.functional.credentials import CredentialsProvider
|
from gceapi.tests.functional import credentials
|
||||||
|
|
||||||
|
|
||||||
class TestSupp(object):
|
CONF = config.CONF.gce
|
||||||
def __init__(self, *args, **kwargs):
|
LOG = logging.getLogger("gceapi")
|
||||||
self._cfg = config.CONF.gce
|
|
||||||
from oslo_log import log as logging
|
|
||||||
self._log = logging.getLogger("gceapi")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def cfg(self):
|
|
||||||
return self._cfg
|
|
||||||
|
|
||||||
def trace(self, *args, **kwargs):
|
|
||||||
self._log.debug(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class LocalRefResolver(RefResolver):
|
def trace(msg):
|
||||||
|
LOG.debug(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def safe_call(method):
|
||||||
|
def wrapper(self, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
return method(self, *args, **kwargs)
|
||||||
|
except Exception as err:
|
||||||
|
trace('Exception {}'.format(err))
|
||||||
|
bt = traceback.format_exc()
|
||||||
|
trace('Exception back trace {}'.format(bt))
|
||||||
|
return None
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class LocalRefResolver(jsonschema.RefResolver):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
base_uri,
|
base_uri,
|
||||||
@ -66,15 +76,14 @@ class LocalRefResolver(RefResolver):
|
|||||||
|
|
||||||
|
|
||||||
class GCEApi(object):
|
class GCEApi(object):
|
||||||
def __init__(self, supp, cred_provider):
|
def __init__(self, cred_provider):
|
||||||
self._compute = None
|
self._compute = None
|
||||||
self._cred_provider = cred_provider
|
self._cred_provider = cred_provider
|
||||||
self._schema = None
|
self._schema = None
|
||||||
self._scheme_ref_resolver = 0
|
self._scheme_ref_resolver = 0
|
||||||
self._supp = supp
|
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
self._schema = Schemas(self._supp.cfg.schema)
|
self._schema = schema.Schemas(CONF.schema)
|
||||||
self._scheme_ref_resolver = LocalRefResolver.from_schema(
|
self._scheme_ref_resolver = LocalRefResolver.from_schema(
|
||||||
self._schema.schemas)
|
self._schema.schemas)
|
||||||
self._build_api()
|
self._build_api()
|
||||||
@ -82,9 +91,8 @@ class GCEApi(object):
|
|||||||
def _build_api(self):
|
def _build_api(self):
|
||||||
credentials = self._cred_provider.credentials
|
credentials = self._cred_provider.credentials
|
||||||
url = self._discovery_url
|
url = self._discovery_url
|
||||||
self._trace(
|
trace('Build Google compute api with discovery url {}'.format(url))
|
||||||
'Build Google compute api with discovery url {}'.format(url))
|
self._compute = discovery.build(
|
||||||
self._compute = build(
|
|
||||||
'compute', 'v1',
|
'compute', 'v1',
|
||||||
credentials=credentials,
|
credentials=credentials,
|
||||||
discoveryServiceUrl=url
|
discoveryServiceUrl=url
|
||||||
@ -92,7 +100,7 @@ class GCEApi(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def _discovery_url(self):
|
def _discovery_url(self):
|
||||||
cfg = self._supp.cfg
|
cfg = CONF
|
||||||
return '{}://{}:{}{}'.format(
|
return '{}://{}:{}{}'.format(
|
||||||
cfg.protocol,
|
cfg.protocol,
|
||||||
cfg.host,
|
cfg.host,
|
||||||
@ -100,9 +108,6 @@ class GCEApi(object):
|
|||||||
cfg.discovery_url
|
cfg.discovery_url
|
||||||
)
|
)
|
||||||
|
|
||||||
def _trace(self, msg):
|
|
||||||
self._supp.trace(msg)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def compute(self):
|
def compute(self):
|
||||||
assert(self._compute is not None)
|
assert(self._compute is not None)
|
||||||
@ -110,7 +115,7 @@ class GCEApi(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def base_url(self):
|
def base_url(self):
|
||||||
cfg = self._supp.cfg
|
cfg = CONF
|
||||||
return '{}://{}:{}'.format(
|
return '{}://{}:{}'.format(
|
||||||
cfg.protocol,
|
cfg.protocol,
|
||||||
cfg.host,
|
cfg.host,
|
||||||
@ -119,27 +124,31 @@ class GCEApi(object):
|
|||||||
|
|
||||||
def validate_schema(self, value, schema_name):
|
def validate_schema(self, value, schema_name):
|
||||||
schema = self._schema.get(schema_name)
|
schema = self._schema.get(schema_name)
|
||||||
validate(value, schema, resolver=self._scheme_ref_resolver)
|
jsonschema.validate(value, schema, resolver=self._scheme_ref_resolver)
|
||||||
|
|
||||||
|
|
||||||
class GCETestCase(base.BaseTestCase):
|
class GCETestCase(base.BaseTestCase):
|
||||||
@property
|
|
||||||
def cfg(self):
|
|
||||||
assert(self._supp.cfg is not None)
|
|
||||||
return self._supp.cfg
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api(self):
|
def api(self):
|
||||||
assert(self._api is not None)
|
assert(self._api is not None)
|
||||||
return self._api
|
return self._api
|
||||||
|
|
||||||
def trace(self, *args, **kwargs):
|
@property
|
||||||
self._supp.trace(*args, **kwargs)
|
def cfg(self):
|
||||||
|
return CONF
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def trace(msg):
|
||||||
|
trace(msg)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def trace_request(request):
|
||||||
|
trace('Request: {}'.format(request.to_json()))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
cls._supp = TestSupp()
|
cp = credentials.CredentialsProvider(CONF)
|
||||||
cls._api = GCEApi(cls._supp, CredentialsProvider(cls._supp))
|
cls._api = GCEApi(cp)
|
||||||
cls._api.init()
|
cls._api.init()
|
||||||
super(GCETestCase, cls).setUpClass()
|
super(GCETestCase, cls).setUpClass()
|
||||||
|
|
||||||
@ -154,49 +163,60 @@ class GCETestCase(base.BaseTestCase):
|
|||||||
self.fail(
|
self.fail(
|
||||||
'There is no required item {} in the list {}'.format(item, items))
|
'There is no required item {} in the list {}'.format(item, items))
|
||||||
|
|
||||||
def _trace_request(self, r):
|
def _get_operations_request(self, name, project, zone, region):
|
||||||
self.trace('Request: {}'.format(r.to_json()))
|
|
||||||
|
|
||||||
def _get_operations_request(self, name, project, zone):
|
|
||||||
if zone is not None:
|
if zone is not None:
|
||||||
return self.api.compute.zoneOperations().get(
|
return self.api.compute.zoneOperations().get(
|
||||||
project=project,
|
project=project,
|
||||||
zone=zone,
|
zone=zone,
|
||||||
operation=name)
|
operation=name)
|
||||||
|
if region is not None:
|
||||||
|
return self.api.compute.regionOperations().get(
|
||||||
|
project=project,
|
||||||
|
region=region,
|
||||||
|
operation=name)
|
||||||
return self.api.compute.globalOperations().get(
|
return self.api.compute.globalOperations().get(
|
||||||
project=project,
|
project=project,
|
||||||
operation=name)
|
operation=name)
|
||||||
|
|
||||||
def _execute_async_request(self, request, project, zone=None):
|
@staticmethod
|
||||||
self._trace_request(request)
|
def _rand_name(prefix='n-'):
|
||||||
|
return data_utils.rand_name(prefix)
|
||||||
|
|
||||||
|
def _add_cleanup(self, method, *args, **kwargs):
|
||||||
|
self.addCleanup(method, *args, **kwargs)
|
||||||
|
|
||||||
|
@safe_call
|
||||||
|
def _remove_cleanup(self, method, *args, **kwargs):
|
||||||
|
v = (method, args, kwargs)
|
||||||
|
self._cleanups.remove(v)
|
||||||
|
|
||||||
|
def _execute_async_request(self, request, project, zone=None, region=None):
|
||||||
|
self.trace_request(request)
|
||||||
operation = request.execute()
|
operation = request.execute()
|
||||||
name = operation['name']
|
name = operation['name']
|
||||||
self.trace('Waiting for operation {} to finish...'.format(name))
|
self.trace('Waiting for operation {} to finish...'.format(name))
|
||||||
begin = time.time()
|
begin = time.time()
|
||||||
timeout = self._supp.cfg.build_timeout
|
timeout = self.cfg.build_timeout
|
||||||
|
result = None
|
||||||
while time.time() - begin < timeout:
|
while time.time() - begin < timeout:
|
||||||
result = self._get_operations_request(
|
result = self._get_operations_request(
|
||||||
name, project, zone).execute()
|
name, project, zone, region).execute()
|
||||||
|
self.api.validate_schema(value=result, schema_name='Operation')
|
||||||
if result['status'] == 'DONE':
|
if result['status'] == 'DONE':
|
||||||
if 'error' in result:
|
if 'error' in result:
|
||||||
self.fail('Request {} failed with error {}'. format(
|
self.fail('Request {} failed with error {}'. format(
|
||||||
name, result['error']))
|
name, result['error']))
|
||||||
else:
|
else:
|
||||||
self.trace("Request {} done successfully".format(name))
|
self.trace("Request {} done successfully".format(name))
|
||||||
return result
|
return
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
self.fail('Request {} failed with timeout {}'.format(name, timeout))
|
self.fail('Request {} failed with timeout {},'
|
||||||
|
' latest operation status {}'.format(name, timeout, result))
|
||||||
|
|
||||||
|
|
||||||
def safe_call(method):
|
def insert_json_parameters(obj, **kwargs):
|
||||||
def wrapper(self, *args, **kwargs):
|
s = json.dumps(obj)
|
||||||
try:
|
t = string.Template(s)
|
||||||
return method(self, *args, **kwargs)
|
s = t.substitute(**kwargs)
|
||||||
except Exception as err:
|
return json.loads(s)
|
||||||
self.trace('Exception {}'.format(err))
|
|
||||||
import traceback
|
|
||||||
bt = traceback.format_exc()
|
|
||||||
self.trace('Exception back trace {}'.format(bt))
|
|
||||||
return None
|
|
||||||
return wrapper
|
|
||||||
|
@ -58,13 +58,14 @@ COMMON_PENDING_OPERATION = {
|
|||||||
}
|
}
|
||||||
COMMON_PENDING_OPERATION.update(COMMON_OPERATION)
|
COMMON_PENDING_OPERATION.update(COMMON_OPERATION)
|
||||||
|
|
||||||
|
REGION = fake_request.REGION
|
||||||
REGION_OPERATION_SPECIFIC = {
|
REGION_OPERATION_SPECIFIC = {
|
||||||
u'id': u'6294142421306477203',
|
u'id': u'5036531165588500177',
|
||||||
u'selfLink': u'http://localhost/compute/v1beta15/projects/'
|
u'selfLink': u'http://localhost/compute/v1beta15/projects/'
|
||||||
'fake_project/regions/RegionOne/operations/'
|
'fake_project/regions/%s/operations/'
|
||||||
'operation-735d48a5-284e-4fb4-a10c-a465ac0b8888',
|
'operation-735d48a5-284e-4fb4-a10c-a465ac0b8888' % REGION,
|
||||||
u'region': u'http://localhost/compute/v1beta15/projects/'
|
u'region': u'http://localhost/compute/v1beta15/projects/'
|
||||||
'fake_project/regions/RegionOne',
|
'fake_project/regions/%s' % REGION,
|
||||||
}
|
}
|
||||||
|
|
||||||
COMMON_REGION_FINISHED_OPERATION = copy.copy(COMMON_FINISHED_OPERATION)
|
COMMON_REGION_FINISHED_OPERATION = copy.copy(COMMON_FINISHED_OPERATION)
|
||||||
|
@ -17,12 +17,12 @@ from gceapi import wsgi_ext as os_wsgi
|
|||||||
|
|
||||||
|
|
||||||
PROJECT_ID = "4a5cc7d8893544a9babb3b890227d75e"
|
PROJECT_ID = "4a5cc7d8893544a9babb3b890227d75e"
|
||||||
|
REGION = u'region-one'
|
||||||
|
|
||||||
FAKE_SERVICE_CATALOG = [{
|
FAKE_SERVICE_CATALOG = [{
|
||||||
u'endpoints': [{
|
u'endpoints': [{
|
||||||
u'adminURL': u'http://192.168.137.21:8774/v2/' + PROJECT_ID,
|
u'adminURL': u'http://192.168.137.21:8774/v2/' + PROJECT_ID,
|
||||||
u'region': u'RegionOne',
|
u'region': REGION,
|
||||||
u'id': u'81a8b36abc5f4945bbd1269be0423012',
|
u'id': u'81a8b36abc5f4945bbd1269be0423012',
|
||||||
u'internalURL': u'http://192.168.137.21:8774/v2/' + PROJECT_ID,
|
u'internalURL': u'http://192.168.137.21:8774/v2/' + PROJECT_ID,
|
||||||
u'publicURL': u'http://192.168.137.21:8774/v2/' + PROJECT_ID}],
|
u'publicURL': u'http://192.168.137.21:8774/v2/' + PROJECT_ID}],
|
||||||
@ -32,7 +32,7 @@ FAKE_SERVICE_CATALOG = [{
|
|||||||
}, {
|
}, {
|
||||||
u'endpoints': [{
|
u'endpoints': [{
|
||||||
u'adminURL': u'http://192.168.137.21:9696/',
|
u'adminURL': u'http://192.168.137.21:9696/',
|
||||||
u'region': u'RegionOne',
|
u'region': REGION,
|
||||||
u'id': u'10a0fc598a5741c390f0d6560a89fced',
|
u'id': u'10a0fc598a5741c390f0d6560a89fced',
|
||||||
u'internalURL': u'http://192.168.137.21:9696/',
|
u'internalURL': u'http://192.168.137.21:9696/',
|
||||||
u'publicURL': u'http://192.168.137.21:9696/'}],
|
u'publicURL': u'http://192.168.137.21:9696/'}],
|
||||||
@ -42,7 +42,7 @@ FAKE_SERVICE_CATALOG = [{
|
|||||||
}, {
|
}, {
|
||||||
u'endpoints': [{
|
u'endpoints': [{
|
||||||
u'adminURL': u'http://192.168.137.21:9292',
|
u'adminURL': u'http://192.168.137.21:9292',
|
||||||
u'region': u'RegionOne',
|
u'region': REGION,
|
||||||
u'id': u'39643060448c4c089535fce07f2d2aa4',
|
u'id': u'39643060448c4c089535fce07f2d2aa4',
|
||||||
u'internalURL': u'http://192.168.137.21:9292',
|
u'internalURL': u'http://192.168.137.21:9292',
|
||||||
u'publicURL': u'http://192.168.137.21:9292'}],
|
u'publicURL': u'http://192.168.137.21:9292'}],
|
||||||
@ -52,7 +52,7 @@ FAKE_SERVICE_CATALOG = [{
|
|||||||
}, {
|
}, {
|
||||||
u'endpoints': [{
|
u'endpoints': [{
|
||||||
u'adminURL': u'http://192.168.137.21:8776/v1/' + PROJECT_ID,
|
u'adminURL': u'http://192.168.137.21:8776/v1/' + PROJECT_ID,
|
||||||
u'region': u'RegionOne',
|
u'region': REGION,
|
||||||
u'id': u'494bd5333aed467092316e03b1163139',
|
u'id': u'494bd5333aed467092316e03b1163139',
|
||||||
u'internalURL': u'http://192.168.137.21:8776/v1/' + PROJECT_ID,
|
u'internalURL': u'http://192.168.137.21:8776/v1/' + PROJECT_ID,
|
||||||
u'publicURL': u'http://192.168.137.21:8776/v1/' + PROJECT_ID}],
|
u'publicURL': u'http://192.168.137.21:8776/v1/' + PROJECT_ID}],
|
||||||
|
@ -13,22 +13,27 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from gceapi.api import addresses
|
from gceapi.api import addresses
|
||||||
from gceapi.tests.unit.api import common
|
|
||||||
|
|
||||||
|
from gceapi.tests.unit.api import common
|
||||||
|
from gceapi.tests.unit.api import fake_request
|
||||||
|
|
||||||
|
|
||||||
|
REGION = fake_request.REGION
|
||||||
EXPECTED_ADDRESSES = [{
|
EXPECTED_ADDRESSES = [{
|
||||||
"kind": "compute#address",
|
"kind": "compute#address",
|
||||||
"id": "4065623605586261056",
|
"id": "1870839154859306350",
|
||||||
"creationTimestamp": "",
|
"creationTimestamp": "",
|
||||||
"status": "IN USE",
|
"status": "IN USE",
|
||||||
"region": "http://localhost/compute/v1beta15/projects/"
|
"region": "http://localhost/compute/v1beta15/projects/"
|
||||||
"fake_project/regions/RegionOne",
|
"fake_project/regions/%s" % REGION,
|
||||||
"name": "address-172-24-4-227",
|
"name": "address-172-24-4-227",
|
||||||
"description": "",
|
"description": "",
|
||||||
"address": "172.24.4.227",
|
"address": "172.24.4.227",
|
||||||
"selfLink": "http://localhost/compute/v1beta15/projects/"
|
"selfLink": "http://localhost/compute/v1beta15/projects/"
|
||||||
"fake_project/regions/RegionOne/addresses/address-172-24-4-227",
|
"fake_project/regions/%s/"
|
||||||
|
"addresses/address-172-24-4-227" % REGION,
|
||||||
"users": ["http://localhost/compute/v1beta15/projects/"
|
"users": ["http://localhost/compute/v1beta15/projects/"
|
||||||
"fake_project/zones/nova/instances/i1"]
|
"fake_project/zones/nova/instances/i1"]
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
|
||||||
@ -40,37 +45,38 @@ class AddressesTest(common.GCEControllerTest):
|
|||||||
|
|
||||||
def test_get_address_by_invalid_name(self):
|
def test_get_address_by_invalid_name(self):
|
||||||
response = self.request_gce("/fake_project/regions/"
|
response = self.request_gce("/fake_project/regions/"
|
||||||
"RegionOne/addresses/fake")
|
"%s/addresses/fake" % REGION)
|
||||||
self.assertEqual(404, response.status_int)
|
self.assertEqual(404, response.status_int)
|
||||||
|
|
||||||
def test_get_address_by_name(self):
|
def test_get_address_by_name(self):
|
||||||
response = self.request_gce("/fake_project/regions/"
|
response = self.request_gce(
|
||||||
"RegionOne/addresses/address-172-24-4-227")
|
"/fake_project/regions/%s/addresses/address-172-24-4-227" % REGION)
|
||||||
|
|
||||||
self.assertEqual(200, response.status_int)
|
self.assertEqual(200, response.status_int)
|
||||||
self.assertEqual(response.json_body, EXPECTED_ADDRESSES[0])
|
self.assertEqual(response.json_body, EXPECTED_ADDRESSES[0])
|
||||||
|
|
||||||
def test_get_address_list_filtered(self):
|
def test_get_address_list_filtered(self):
|
||||||
response = self.request_gce("/fake_project/regions/RegionOne/addresses"
|
response = self.request_gce("/fake_project/regions/%s/addresses"
|
||||||
"?filter=name+eq+address-172-24-4-227")
|
"?filter=name+eq+address-172-24-4-227" %
|
||||||
|
REGION)
|
||||||
expected = {
|
expected = {
|
||||||
"kind": "compute#addressList",
|
"kind": "compute#addressList",
|
||||||
"id": "projects/fake_project/regions/RegionOne/addresses",
|
"id": "projects/fake_project/regions/%s/addresses" % REGION,
|
||||||
"selfLink": "http://localhost/compute/v1beta15/projects"
|
"selfLink": "http://localhost/compute/v1beta15/projects"
|
||||||
"/fake_project/regions/RegionOne/addresses",
|
"/fake_project/regions/%s/addresses" % REGION,
|
||||||
"items": [EXPECTED_ADDRESSES[0]]
|
"items": [EXPECTED_ADDRESSES[0]]
|
||||||
}
|
}
|
||||||
|
|
||||||
self.assertEqual(response.json_body, expected)
|
self.assertEqual(response.json_body, expected)
|
||||||
|
|
||||||
def test_get_address_list(self):
|
def test_get_address_list(self):
|
||||||
response = self.request_gce("/fake_project/regions/RegionOne"
|
response = self.request_gce("/fake_project/regions/%s"
|
||||||
"/addresses")
|
"/addresses" % REGION)
|
||||||
expected = {
|
expected = {
|
||||||
"kind": "compute#addressList",
|
"kind": "compute#addressList",
|
||||||
"id": "projects/fake_project/regions/RegionOne/addresses",
|
"id": "projects/fake_project/regions/%s/addresses" % REGION,
|
||||||
"selfLink": "http://localhost/compute/v1beta15/projects"
|
"selfLink": "http://localhost/compute/v1beta15/projects"
|
||||||
"/fake_project/regions/RegionOne/addresses",
|
"/fake_project/regions/%s/addresses" % REGION,
|
||||||
"items": EXPECTED_ADDRESSES
|
"items": EXPECTED_ADDRESSES
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +92,7 @@ class AddressesTest(common.GCEControllerTest):
|
|||||||
"selfLink": "http://localhost/compute/v1beta15/projects"
|
"selfLink": "http://localhost/compute/v1beta15/projects"
|
||||||
"/fake_project/aggregated/addresses",
|
"/fake_project/aggregated/addresses",
|
||||||
"items": {
|
"items": {
|
||||||
"regions/RegionOne": {
|
"regions/%s" % REGION: {
|
||||||
"addresses": [EXPECTED_ADDRESSES[0]]
|
"addresses": [EXPECTED_ADDRESSES[0]]
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -103,7 +109,7 @@ class AddressesTest(common.GCEControllerTest):
|
|||||||
"selfLink": "http://localhost/compute/v1beta15/projects"
|
"selfLink": "http://localhost/compute/v1beta15/projects"
|
||||||
"/fake_project/aggregated/addresses",
|
"/fake_project/aggregated/addresses",
|
||||||
"items": {
|
"items": {
|
||||||
"regions/RegionOne": {
|
"regions/%s" % REGION: {
|
||||||
"addresses": EXPECTED_ADDRESSES
|
"addresses": EXPECTED_ADDRESSES
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -112,21 +118,21 @@ class AddressesTest(common.GCEControllerTest):
|
|||||||
self.assertEqual(response.json_body, expected)
|
self.assertEqual(response.json_body, expected)
|
||||||
|
|
||||||
def test_delete_address_with_invalid_name(self):
|
def test_delete_address_with_invalid_name(self):
|
||||||
response = self.request_gce("/fake_project/regions/RegionOne"
|
response = self.request_gce(
|
||||||
"/addresses/fake-address", method="DELETE")
|
"/fake_project/regions/%s/addresses/fake-address" % REGION,
|
||||||
|
method="DELETE")
|
||||||
self.assertEqual(404, response.status_int)
|
self.assertEqual(404, response.status_int)
|
||||||
|
|
||||||
def test_delete_address(self):
|
def test_delete_address(self):
|
||||||
response = self.request_gce(
|
response = self.request_gce(
|
||||||
"/fake_project/regions/RegionOne/"
|
"/fake_project/regions/%s/addresses/address-172-24-4-227" % REGION,
|
||||||
"addresses/address-172-24-4-227",
|
method="DELETE")
|
||||||
method="DELETE")
|
|
||||||
expected = {
|
expected = {
|
||||||
"operationType": "delete",
|
"operationType": "delete",
|
||||||
"targetId": "4065623605586261056",
|
"targetId": "1870839154859306350",
|
||||||
"targetLink": "http://localhost/compute/v1beta15/projects/"
|
"targetLink": "http://localhost/compute/v1beta15/projects/"
|
||||||
"fake_project/regions/RegionOne/"
|
"fake_project/regions/%s/"
|
||||||
"addresses/address-172-24-4-227",
|
"addresses/address-172-24-4-227" % REGION,
|
||||||
}
|
}
|
||||||
expected.update(common.COMMON_REGION_FINISHED_OPERATION)
|
expected.update(common.COMMON_REGION_FINISHED_OPERATION)
|
||||||
self.assertEqual(200, response.status_int)
|
self.assertEqual(200, response.status_int)
|
||||||
@ -136,16 +142,17 @@ class AddressesTest(common.GCEControllerTest):
|
|||||||
request_body = {
|
request_body = {
|
||||||
"name": "fake-address",
|
"name": "fake-address",
|
||||||
}
|
}
|
||||||
response = self.request_gce("/fake_project/regions/RegionOne/"
|
response = self.request_gce("/fake_project/regions/%s/"
|
||||||
"addresses",
|
"addresses" % REGION,
|
||||||
method="POST",
|
method="POST",
|
||||||
body=request_body)
|
body=request_body)
|
||||||
self.assertEqual(200, response.status_int)
|
self.assertEqual(200, response.status_int)
|
||||||
expected = {
|
expected = {
|
||||||
"operationType": "insert",
|
"operationType": "insert",
|
||||||
"targetId": "4570437344333712421",
|
"targetId": "8754519975833457287",
|
||||||
"targetLink": "http://localhost/compute/v1beta15/projects/"
|
"targetLink": "http://localhost/compute/v1beta15/projects/"
|
||||||
"fake_project/regions/RegionOne/addresses/fake-address",
|
"fake_project/regions/%s/addresses/fake-address" %
|
||||||
|
REGION,
|
||||||
}
|
}
|
||||||
expected.update(common.COMMON_REGION_FINISHED_OPERATION)
|
expected.update(common.COMMON_REGION_FINISHED_OPERATION)
|
||||||
self.assertDictEqual(expected, response.json_body)
|
self.assertDictEqual(expected, response.json_body)
|
||||||
|
@ -14,19 +14,22 @@
|
|||||||
|
|
||||||
from gceapi.api import regions
|
from gceapi.api import regions
|
||||||
from gceapi.tests.unit.api import common
|
from gceapi.tests.unit.api import common
|
||||||
|
from gceapi.tests.unit.api import fake_request
|
||||||
|
|
||||||
|
|
||||||
|
REGION = fake_request.REGION
|
||||||
EXPECTED_REGIONS = [
|
EXPECTED_REGIONS = [
|
||||||
{
|
{
|
||||||
"id": "1905250285734383880",
|
"id": "8220497844553564918",
|
||||||
"kind": "compute#region",
|
"kind": "compute#region",
|
||||||
"selfLink": "http://localhost/compute/v1beta15/projects/fake_project"
|
"selfLink": "http://localhost/compute/v1beta15/projects/fake_project"
|
||||||
"/regions/RegionOne",
|
"/regions/%s" % REGION,
|
||||||
"name": "RegionOne",
|
"name": REGION,
|
||||||
"status": "UP",
|
"status": "UP",
|
||||||
"zones": [
|
"zones": [
|
||||||
"http://localhost/compute/v1beta15/projects/fake_project"
|
"http://localhost/compute/v1beta15/projects/fake_project"
|
||||||
"/zones/nova"]
|
"/zones/nova"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -46,14 +49,14 @@ class RegionsTest(common.GCEControllerTest):
|
|||||||
self.assertEqual(404, response.status_int)
|
self.assertEqual(404, response.status_int)
|
||||||
|
|
||||||
def test_get_region(self):
|
def test_get_region(self):
|
||||||
response = self.request_gce('/fake_project/regions/RegionOne')
|
response = self.request_gce('/fake_project/regions/%s' % REGION)
|
||||||
expected = EXPECTED_REGIONS[0]
|
expected = EXPECTED_REGIONS[0]
|
||||||
|
|
||||||
self.assertEqual(response.json_body, expected)
|
self.assertEqual(response.json_body, expected)
|
||||||
|
|
||||||
def test_get_region_list_filtered(self):
|
def test_get_region_list_filtered(self):
|
||||||
response = self.request_gce("/fake_project/regions"
|
response = self.request_gce("/fake_project/regions"
|
||||||
"?filter=name+eq+RegionOne")
|
"?filter=name+eq+%s" % REGION)
|
||||||
expected = {
|
expected = {
|
||||||
"kind": "compute#regionList",
|
"kind": "compute#regionList",
|
||||||
"id": "projects/fake_project/regions",
|
"id": "projects/fake_project/regions",
|
||||||
|
@ -14,16 +14,18 @@
|
|||||||
|
|
||||||
from gceapi.api import zones
|
from gceapi.api import zones
|
||||||
from gceapi.tests.unit.api import common
|
from gceapi.tests.unit.api import common
|
||||||
|
from gceapi.tests.unit.api import fake_request
|
||||||
|
|
||||||
|
|
||||||
|
REGION = fake_request.REGION
|
||||||
EXPECTED_ZONES = [{
|
EXPECTED_ZONES = [{
|
||||||
"id": "3924463100986466035",
|
"id": "3924463100986466035",
|
||||||
"kind": "compute#zone",
|
"kind": "compute#zone",
|
||||||
"selfLink": "http://localhost/compute/v1beta15/projects/fake_project"
|
"selfLink": "http://localhost/compute/v1beta15/projects/fake_project"
|
||||||
"/zones/nova",
|
"/zones/nova",
|
||||||
"name": "nova",
|
"name": "nova",
|
||||||
"status": "UP",
|
"status": "UP",
|
||||||
"region": "RegionOne",
|
"region": REGION,
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user