Added simple instances tests (CRUD)

Change-Id: Id4c9f7c4b18d6d2e3bd74c728d555f9513f08420
This commit is contained in:
alexey-mr 2015-10-26 19:16:28 +03:00
parent 7f983ef92c
commit 3ecaef77b7
22 changed files with 548 additions and 75 deletions

View File

@ -59,6 +59,11 @@ now, 8-byte hashes are generated and returned for any ID to report.
* GCE allows per-user SSH key specification, but Nova supports only one 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,
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 'n1-standard-1' to 'n1.standard.1').
Authentication specifics
========================

View File

@ -171,11 +171,12 @@ function configure_gceapi {
#-------------------------
iniset $GCEAPI_CONF_FILE DEFAULT region $REGION_NAME
iniset $GCEAPI_CONF_FILE DEFAULT keystone_url "$OS_AUTH_URL"
iniset $GCEAPI_CONF_FILE DEFAULT admin_tenant_name $SERVICE_TENANT_NAME
iniset $GCEAPI_CONF_FILE DEFAULT admin_user $GCEAPI_ADMIN_USER
iniset $GCEAPI_CONF_FILE DEFAULT admin_password $SERVICE_PASSWORD
iniset $GCEAPI_CONF_FILE DEFAULT identity_uri "http://${KEYSTONE_AUTH_HOST}:35357/v2.0"
iniset $GCEAPI_CONF_FILE keystone_authtoken admin_tenant_name $SERVICE_TENANT_NAME
iniset $GCEAPI_CONF_FILE keystone_authtoken admin_user $GCEAPI_ADMIN_USER
iniset $GCEAPI_CONF_FILE keystone_authtoken admin_password $SERVICE_PASSWORD
iniset $GCEAPI_CONF_FILE keystone_authtoken identity_uri "$OS_AUTH_URL"
configure_gceapi_rpc_backend

74
doc/source/conf.py Normal file
View File

@ -0,0 +1,74 @@
from __future__ import print_function
import sys
import os
import fileinput
import fnmatch
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", ".."))
sys.path.insert(0, ROOT)
sys.path.insert(0, BASE_DIR)
# This is required for ReadTheDocs.org, but isn't a bad idea anyway.
os.environ['DJANGO_SETTINGS_MODULE'] = 'openstack_dashboard.settings'
# -- General configuration ----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo',
'sphinx.ext.viewcode']
# autodoc generation is a bit aggressive and a nuisance when doing heavy
# text edit cycles.
# execute "export SPHINX_DEBUG=1" in your terminal to disable
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'gce-api'
copyright = '2015, OpenStack Foundation'
# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
add_module_names = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# -- Options for HTML output --------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
#html_theme_path = ["."]
#html_theme = '_theme'
#html_static_path = ['static']
# Output file base name for HTML help builder.
htmlhelp_basename = '%sdoc' % project
git_cmd = "git log --pretty=format:'%ad, commit %h' --date=local -n1"
html_last_updated_fmt = os.popen(git_cmd).read()
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass
# [howto/manual]).
latex_documents = [
('index',
'%s.tex' % project,
'%s Documentation' % project,
'OpenStack Foundation', 'manual'),
]

1
doc/source/hacking.rst Normal file
View File

@ -0,0 +1 @@
.. include:: ../../HACKING.rst

22
doc/source/index.rst Normal file
View File

@ -0,0 +1,22 @@
OpenStack GCE API
=====================
Support of GCE API for OpenStack.
This project provides a standalone GCE API service that enables
managing of OpenStack Nova service in a manner of Google Cloud Compute
Engine.
It uses port 8787 by default that could be changed via config file..
Contents:
.. toctree::
:maxdepth: 1
hacking
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -40,7 +40,7 @@ gce_opts = [
cfg.StrOpt('network_api',
default="neutron",
help='Name of network API. neutron(quantum) or nova'),
cfg.StrOpt('keystone_gce_url',
cfg.StrOpt('keystone_url',
default='http://127.0.0.1:5000/v2.0',
help='Keystone URL'),
cfg.StrOpt('public_network',

View File

@ -11,7 +11,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from keystoneclient.v2_0 import client as kc
from keystoneclient import client as kc
from novaclient import client as novaclient
from novaclient import exceptions as nova_exception
from oslo_config import cfg
@ -52,7 +52,7 @@ _nova_api_version = None
def nova(context, service_type='compute'):
args = {
'auth_url': CONF.keystone_gce_url,
'auth_url': CONF.keystone_url,
'auth_token': context.auth_token,
'bypass_url': url_for(context, service_type),
}
@ -67,7 +67,7 @@ def neutron(context):
return None
args = {
'auth_url': CONF.keystone_gce_url,
'auth_url': CONF.keystone_url,
'service_type': 'network',
'token': context.auth_token,
'endpoint_url': url_for(context, 'network'),
@ -81,7 +81,7 @@ def glance(context):
return None
args = {
'auth_url': CONF.keystone_gce_url,
'auth_url': CONF.keystone_url,
'service_type': 'image',
'token': context.auth_token,
}
@ -96,7 +96,7 @@ def cinder(context):
args = {
'service_type': 'volume',
'auth_url': CONF.keystone_gce_url,
'auth_url': CONF.keystone_url,
'username': None,
'api_key': None,
}
@ -110,18 +110,24 @@ def cinder(context):
def keystone(context):
return kc.Client(
c = kc.Client(
token=context.auth_token,
project_id=context.project_id,
tenant_id=context.project_id,
auth_url=CONF.keystone_gce_url)
auth_url=CONF.keystone_url)
if c.auth_ref is None:
# Ver2 doesn't create session and performs
# authentication automatically, but Ver3 does create session
# if it's not provided and doesn't perform authentication.
# TODO(use sessions)
c.authenticate()
return c
def url_for(context, service_type):
service_catalog = context.service_catalog
if not service_catalog:
catalog = keystone(context).service_catalog.catalog
service_catalog = catalog['serviceCatalog']
service_catalog = keystone(context).service_catalog.get_data()
context.service_catalog = service_catalog
return get_url_from_catalog(service_catalog, service_type)

View File

@ -17,7 +17,7 @@ import os
import threading
import webob
from keystoneclient.v2_0 import client as keystone_client
from keystoneclient import client as keystone_client
from oslo_config import cfg
from oslo_log import log as logging
@ -25,7 +25,7 @@ from gceapi.api import clients
from gceapi import wsgi_ext as openstack_wsgi
LOG = logging.getLogger(__name__)
FLAGS = cfg.CONF
CONF = cfg.CONF
class Controller(object):
@ -40,12 +40,20 @@ class Controller(object):
if key in self._files:
return self._files[key]
tenant = FLAGS.keystone_authtoken["admin_tenant_name"]
user = FLAGS.keystone_authtoken["admin_user"]
password = FLAGS.keystone_authtoken["admin_password"]
keystone = keystone_client.Client(username=user, password=password,
tenant_name=tenant, auth_url=FLAGS.keystone_gce_url)
catalog = keystone.service_catalog.catalog["serviceCatalog"]
auth_data = {
'project_name': CONF.keystone_authtoken['admin_tenant_name'],
'username': CONF.keystone_authtoken['admin_user'],
'password': CONF.keystone_authtoken['admin_password'],
'auth_url': CONF.keystone_url,
}
keystone = keystone_client.Client(**auth_data)
if keystone.auth_ref is None:
# Ver2 doesn't create session and performs
# authentication automatically, but Ver3 does create session
# if it's not provided and doesn't perform authentication.
# TODO(use sessions)
keystone.authenticate()
catalog = keystone.service_catalog.get_data()
public_url = clients.get_url_from_catalog(catalog, "gceapi")
if not public_url:
public_url = req.host_url
@ -66,7 +74,7 @@ class Controller(object):
def _load_file(self, version):
file = version + ".json"
protocol_dir = FLAGS.get("protocol_dir")
protocol_dir = CONF.get("protocol_dir")
if protocol_dir:
file_name = os.path.join(protocol_dir, file)
try:

View File

@ -393,7 +393,6 @@ class API(base_api.API):
instance = context.operation_data.get("instance")
progress = {"progress": int(100.0 * disk_device / full_count)}
disk_device = context.operation_data["disk_device"]
disk = context.operation_data.get("disk")
if disk:
volume_id = disk["id"]
@ -421,8 +420,14 @@ class API(base_api.API):
body, scope=scope)
disk["id"] = volume["id"]
context.operation_data["disk"] = disk
device_name = "vd" + string.ascii_lowercase[disk_device]
# deviceName is optional parameter
# use passed value if given, othewise generate new dev name
device_name = disk.get("deviceName")
if device_name is None:
device_name = "vd" + string.ascii_lowercase[disk_device]
disk["deviceName"] = device_name
bdm[device_name] = disk["id"]
if "initializeParams" in disk:
return progress
disk_device += 1

View File

@ -17,17 +17,16 @@ import json
import time
import uuid
from keystoneclient import client as keystone_client
from keystoneclient import exceptions
from keystoneclient.v2_0 import client as keystone_client
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import timeutils
import webob
from gceapi.i18n import _
from gceapi import wsgi_ext as openstack_wsgi
FLAGS = cfg.CONF
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
@ -150,11 +149,16 @@ class Controller(object):
keystone = keystone_client.Client(
username=username,
password=password,
auth_url=FLAGS.keystone_gce_url)
token = keystone.auth_ref["token"]
client.auth_token = token["id"]
s = timeutils.parse_isotime(token["issued_at"])
e = timeutils.parse_isotime(token["expires"])
auth_url=CONF.keystone_url)
if keystone.auth_ref is None:
# Ver2 doesn't create session and performs
# authentication automatically, but Ver3 does create session
# if it's not provided and doesn't perform authentication.
# TODO(use sessions)
keystone.authenticate()
client.auth_token = keystone.auth_token
s = keystone.auth_ref.issued
e = keystone.auth_ref.expires
client.expires_in = (e - s).seconds
except Exception as ex:
return webob.exc.HTTPUnauthorized(ex)
@ -201,7 +205,7 @@ class AuthProtocol(object):
"""Filter for translating oauth token to keystone token."""
def __init__(self, app):
self.app = app
self.keystone_url = FLAGS.keystone_gce_url
self.auth_url = CONF.keystone_url
def __call__(self, env, start_response):
auth_token = env.get("HTTP_AUTHORIZATION")
@ -214,8 +218,15 @@ class AuthProtocol(object):
token=auth_token.split()[1],
tenant_name=project,
force_new_token=True,
auth_url=self.keystone_url)
env["HTTP_X_AUTH_TOKEN"] = keystone.auth_ref["token"]["id"]
auth_url=self.auth_url)
if keystone.auth_ref is None:
# Ver2 doesn't create session and performs
# authentication automatically, but Ver3 does create session
# if it's not provided and doesn't perform authentication.
# TODO(use sessions)
keystone.authenticate()
scoped_token = keystone.auth_token
env["HTTP_X_AUTH_TOKEN"] = scoped_token
return self.app(env, start_response)
except exceptions.Unauthorized:
if project in INTERNAL_GCUTIL_PROJECTS:

View File

@ -20,6 +20,7 @@ from oslo_db.sqlalchemy import models
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Index, PrimaryKeyConstraint, String, Text
BASE = declarative_base()
@ -34,3 +35,10 @@ class Item(BASE, models.ModelBase):
kind = Column(String(length=50))
name = Column(String(length=63))
data = Column(Text())
def save(self, session=None):
from gceapi.db.sqlalchemy import api
if session is None:
session = api.get_session()
super(Item, self).save(session=session)

View File

@ -37,22 +37,27 @@ if [[ ! -f $TEST_CONFIG_DIR/$TEST_CONFIG ]]; then
exit 1
fi
# prepare flavors
nova flavor-create --is-public True m1.gceapi 16 512 0 1
# create separate user/project
project_name="project-$(cat /dev/urandom | tr -cd 'a-f0-9' | head -c 8)"
eval $(openstack project create -f shell -c id $project_name)
project_id=$id
[[ -n "$project_id" ]] || { echo "Can't create project"; exit 1; }
user_name="user-$(cat /dev/urandom | tr -cd 'a-f0-9' | head -c 8)"
password='qwe123QWE'
password='password'
eval $(openstack user create "$user_name" --project "$project_id" --password "$password" --email "$user_name@example.com" -f shell -c id)
user_id=$id
[[ -n "$user_id" ]] || { echo "Can't create user"; exit 1; }
# add 'Member' role for swift access
role_id=$(openstack role show Member -c id -f value)
openstack role add --project $project_id --user $user_id $role_id
# prepare flavors
flavor_name="n1.standard.1"
if [[ -z "$(nova flavor-list | grep $flavor_name)" ]]; then
nova flavor-create --is-public True $flavor_name 16 512 0 1
[[ "$?" -eq 0 ]] || { echo "Failed to prepare flavor"; exit 1; }
fi
# create network
if [[ -n $(openstack service list | grep neutron) ]]; then
net_id=$(neutron net-create --tenant-id $project_id "private" | grep ' id ' | awk '{print $4}')
@ -68,6 +73,19 @@ if [[ ! -f $TEST_CONFIG_DIR/$TEST_CONFIG ]]; then
neutron router-gateway-set $router_id $public_net_id
[[ "$?" -eq 0 ]] || { echo "router-gateway-set failed"; exit 1; }
fi
#create image in raw format
os_image_name="cirros-0.3.4-raw-image"
if [[ -z "$(openstack image list | grep $os_image_name)" ]]; then
image_name="cirros-0.3.4-x86_64-disk.img"
cirros_image_url="http://download.cirros-cloud.net/0.3.4/$image_name"
sudo rm -f /tmp/$image_name
wget -nv -P /tmp $cirros_image_url
[[ "$?" -eq 0 ]] || { echo "Failed to download image"; exit 1; }
openstack image create --disk-format raw --container-format bare --public --file "/tmp/$image_name" $os_image_name
[[ "$?" -eq 0 ]] || { echo "Failed to prepare image"; exit 1; }
fi
export OS_PROJECT_NAME=$project_name
export OS_TENANT_NAME=$project_name
export OS_USERNAME=$user_name
@ -76,7 +94,8 @@ if [[ ! -f $TEST_CONFIG_DIR/$TEST_CONFIG ]]; then
sudo bash -c "cat > $TEST_CONFIG_DIR/$TEST_CONFIG <<EOF
[gce]
# Generic options
build_interval=${TIMEOUT:-180}
build_timeout=${TIMEOUT:-180}
build_interval=1
# GCE API schema
schema=${GCE_SCHEMA:-'etc/gceapi/protocols/v1.json'}
@ -99,12 +118,16 @@ discovery_url=${GCE_DISCOVERY_URL:-'/discovery/v1/apis/{api}/{apiVersion}/rest'}
project_id=${OS_PROJECT_NAME}
zone=${ZONE:-'nova'}
region=${REGION:-'RegionOne'}
# convert flavor name: becase GCE dowsn't allows '.' and converts '-' into '.'
machine_type=${flavor_name//\./-}
image=${os_image_name}
EOF"
fi
sudo pip install -r test-requirements.txt
sudo pip install google-api-python-client
sudo OS_STDOUT_CAPTURE=-1 OS_STDERR_CAPTURE=-1 OS_TEST_TIMEOUT=500 OS_TEST_LOCK_PATH=${TMPDIR:-'/tmp'} \
python -m subunit.run discover -t ./ ./gceapi/tests/functional | subunit-2to1 | tools/colorizer.py
python -m subunit.run discover -t ./ ./gceapi/tests/functional/api | subunit-2to1 | tools/colorizer.py
RETVAL=$?
# Here can be some commands for log archiving, etc...
@ -120,8 +143,9 @@ export OS_PROJECT_NAME=$OLD_OS_PROJECT_NAME
export OS_TENANT_NAME=$OLD_OS_PROJECT_NAME
export OS_USERNAME=$OLD_OS_USERNAME
export OS_PASSWORD=$OLD_OS_PASSWORD
openstack server list --all-projects
openstack flavor list
openstack image list
openstack server list --all-projects
openstack volume list --all-projects
cinder snapshot-list --all-tenants

View File

@ -0,0 +1,232 @@
# 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 string import Template
from json import dumps
from json import loads
from gceapi.tests.functional import test_base
BASE_COMPUTE_URL = '{address}/compute/v1'
CREATE_INSTANCE_TEMPLATE = {
"name": "${instance}",
"description": "Testing instance",
"machineType": "zones/${zone}/machineTypes/${machine_type}",
"disks": [
{
"boot": True,
"autoDelete": True,
"initializeParams": {
"sourceImage": "projects/${image}",
}
}
],
"networkInterfaces": [
{
"network": "global/networks/${network}",
}
],
"metadata": {
"items": [
{
"key": "test_metadata_key",
"value": "test_metadata_value"
},
{
"key": "startup-script",
"value": "echo Test startup script"
}
]
},
"serviceAccounts": [
{
"email": "default",
"scopes": [
"https://www.googleapis.com/auth/cloud.useraccounts.readonly",
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring.write"
]
}
]
}
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):
s = dumps(obj)
t = Template(s)
s = t.substitute(**kwargs)
return loads(s)
def _prepare_instace_insert_parameters(**kwargs):
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
def instances(self):
res = self.api.compute.instances()
self.assertIsNotNone(
res,
'Null instances object, api is not built properly')
return res
@property
def networks(self):
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
project_id = cfg.project_id
zone = cfg.zone
kw = {
'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))
request = self.instances.insert(
project=project_id,
zone=zone,
body=config)
result = self._execute_async_request(request, project_id, zone=zone)
self.api.validate_schema(value=result, schema_name='Operation')
return result
def _delete_instance(self):
cfg = self.cfg
project_id = cfg.project_id
zone = cfg.zone
instance = self._instance_name
self.trace(
'Delete instance: project_id={} zone={} instance {}'.
format(project_id, zone, instance))
request = self.instances.delete(
project=project_id,
zone=zone,
instance=instance)
result = self._execute_async_request(request, project_id, zone=zone)
self.api.validate_schema(value=result, schema_name='Operation')
return result
def _list(self):
project_id = self.cfg.project_id
zone = self.cfg.zone
self.trace(
'List instances: project_id={} zone={}'.format(project_id, zone))
request = self.instances.list(project=project_id, zone=zone)
self._trace_request(request)
result = request.execute()
self.trace('Instances: {}'.format(result))
self.api.validate_schema(value=result, schema_name='InstanceList')
self.assertFind(self._instance_name, result)
return result
def _get(self):
project_id = self.cfg.project_id
zone = self.cfg.zone
instance = self._instance_name
self.trace(
'Get instance: project_id={} zone={} instance={}'.
format(project_id, zone, instance))
request = self.instances.get(
project=project_id,
zone=zone,
instance=instance)
result = request.execute()
self.trace('Instance: {}'.format(result))
self.api.validate_schema(value=result, schema_name='Instance')
return result
class TestIntancesCRUD(TestIntancesBase):
def _create(self):
self._create_network()
self._create_instance()
def _read(self):
self._get()
self._list()
def _update(self):
#TODO(to impl simple update cases)
pass
def _delete(self):
self._delete_instance()
self._delete_network()
def test_crud(self):
self._create()
self._read()
self._update()
self._delete()

View File

@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import unittest
from gceapi.tests.functional import test_base
@ -33,6 +34,8 @@ class TestRegions(test_base.GCETestCase):
'Null regions object, api is not built properly')
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):
project_id = self.cfg.project_id
region = self.cfg.region

View File

@ -193,6 +193,8 @@ class GCESmokeTestCase(testtools.TestCase):
if GCESmokeTestCase.failed:
raise unittest.SkipTest("Skipped by previous exception")
super(GCESmokeTestCase, self).setUp()
self.skipTest('Not to run in gating. It is just an old example and '
'will be remove removed in future')
def wait_for_operation(self, body, operation, status):
self.assertEqual("compute#operation", body["kind"])

View File

@ -40,7 +40,7 @@ OPTIONS = [
default='demo',
help='User name'),
cfg.StrOpt('password',
default='qwe123QWE',
default='password',
help='User password'),
cfg.StrOpt('auth_url',
default='http://localhost:5000/v2.0/',
@ -81,6 +81,13 @@ OPTIONS = [
cfg.StrOpt('region',
default='RegionOne',
help='GCE Region for testing'),
cfg.StrOpt('machine_type',
default='n1-standard-1',
help='Machine type - a type of instance ot be created'),
cfg.StrOpt('image',
default='debian-cloud/global/images/debian-7-wheezy-v20150929',
help='Image to create instances'),
]

View File

@ -32,6 +32,13 @@ class CredentialsProvider(object):
return GoogleCredentials.get_application_default()
def _get_token_crenetials(self):
client = self._create_keystone_client()
token = client.auth_token
self._trace('Created token {}'.format(token))
return AccessTokenCredentials(access_token=token,
user_agent='GCE test')
def _create_keystone_client(self):
cfg = self._supp.cfg
auth_data = {
'username': cfg.username,
@ -39,14 +46,15 @@ class CredentialsProvider(object):
'tenant_name': cfg.project_id,
'auth_url': cfg.auth_url
}
self._trace('Auth data {}'.format(auth_data))
self._trace('Create keystone client, auth_data={}'.format(auth_data))
client = KeystoneClient(**auth_data)
if not client.authenticate():
raise Exception('Failed to authenticate user')
token = client.auth_token
self._trace('Created token {}'.format(token))
return AccessTokenCredentials(access_token=token,
user_agent='GCE test')
return client
@property
def keystone_client(self):
return self._create_keystone_client()
@property
def credentials(self):

View File

@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import time
from googleapiclient.discovery import build
from googleapiclient.schema import Schemas
@ -36,8 +37,7 @@ class TestSupp(object):
return self._cfg
def trace(self, *args, **kwargs):
print(args, kwargs)
self._log.trace(*args, **kwargs)
self._log.debug(*args, **kwargs)
class LocalRefResolver(RefResolver):
@ -108,6 +108,15 @@ class GCEApi(object):
assert(self._compute is not None)
return self._compute
@property
def base_url(self):
cfg = self._supp.cfg
return '{}://{}:{}'.format(
cfg.protocol,
cfg.host,
cfg.port
)
def validate_schema(self, value, schema_name):
schema = self._schema.get(schema_name)
validate(value, schema, resolver=self._scheme_ref_resolver)
@ -135,13 +144,59 @@ class GCETestCase(base.BaseTestCase):
super(GCETestCase, cls).setUpClass()
def assertFind(self, item, items_list):
found = False
items = items_list['items']
for i in items:
if i['name'] == item:
found = True
break
self.assertTrue(
found,
key = 'items'
items = []
if key in items_list:
items = items_list[key]
for i in items:
if i['name'] == item:
return
self.fail(
'There is no required item {} in the list {}'.format(item, items))
def _trace_request(self, r):
self.trace('Request: {}'.format(r.to_json()))
def _get_operations_request(self, name, project, zone):
if zone is not None:
return self.api.compute.zoneOperations().get(
project=project,
zone=zone,
operation=name)
return self.api.compute.globalOperations().get(
project=project,
operation=name)
def _execute_async_request(self, request, project, zone=None):
self._trace_request(request)
operation = request.execute()
name = operation['name']
self.trace('Waiting for operation {} to finish...'.format(name))
begin = time.time()
timeout = self._supp.cfg.build_timeout
while time.time() - begin < timeout:
result = self._get_operations_request(
name, project, zone).execute()
if result['status'] == 'DONE':
if 'error' in result:
self.fail('Request {} failed with error {}'. format(
name, result['error']))
else:
self.trace("Request {} done successfully".format(name))
return result
time.sleep(1)
self.fail('Request {} failed with timeout {}'.format(name, timeout))
def safe_call(method):
def wrapper(self, *args, **kwargs):
try:
return method(self, *args, **kwargs)
except Exception as err:
self.trace('Exception {}'.format(err))
import traceback
bt = traceback.format_exc()
self.trace('Exception back trace {}'.format(bt))
return None
return wrapper

View File

@ -15,14 +15,15 @@
# under the License.
import itertools
import netaddr
from oslo_log import log as logging
import testtools
from oslo_log import log as logging
from tempest.common.utils import data_utils
from tempest import config
import tempest.thirdparty.gce.base as base_gce
from gceapi.tests.functional import base as base_gce
CONF = config.CONF
LOG = logging.getLogger("tempest.thirdparty.gce")

View File

@ -17,7 +17,7 @@ import uuid
from cinderclient import client as cinderclient
from glanceclient import client as glanceclient
from keystoneclient.v2_0 import client as kc
from keystoneclient import client as kc
from neutronclient.v2_0 import client as neutronclient
from novaclient import client as novaclient
from oslo_utils import timeutils

View File

@ -29,6 +29,10 @@ class FakeTenants(object):
return FAKE_PROJECTS
class FakeAccessInfo(object):
pass
class FakeKeystoneClient(object):
def __init__(self, **kwargs):
pass
@ -36,3 +40,7 @@ class FakeKeystoneClient(object):
@property
def tenants(self):
return FakeTenants()
@property
def auth_ref(self):
return FakeAccessInfo()

View File

@ -210,32 +210,24 @@ if [ ! -s $APIPASTE_FILE ]; then
fi
sudo cp -nR etc/gceapi/protocols $CONF_DIR
AUTH_HOST=${OS_AUTH_URL#*//}
AUTH_HOST=${AUTH_HOST%:*}
AUTH_CACHE_DIR=${AUTH_CACHE_DIR:-/var/cache/gceapi}
AUTH_PORT=`keystone catalog|grep -A 9 identity|grep adminURL|awk '{print $4}'`
AUTH_PORT=${AUTH_PORT##*:}
AUTH_PORT=${AUTH_PORT%%/*}
AUTH_PROTO=${OS_AUTH_URL%%:*}
PUBLIC_URL=${OS_AUTH_URL%:*}:8787/
#update default config with some values
iniset $CONF_FILE DEFAULT api_paste_config $APIPASTE_FILE
iniset $CONF_FILE DEFAULT logging_context_format_string "%(asctime)s.%(msecs)03d %(levelname)s %(name)s [%(request_id)s %(user_name)s %(project_name)s] %(instance)s%(message)s"
iniset $CONF_FILE DEFAULT verbose True
iniset $CONF_FILE DEFAULT keystone_gce_url "$OS_AUTH_URL"
iniset $CONF_FILE DEFAULT network_api "$NETWORK_API"
iniset $CONF_FILE DEFAULT region "$REGION"
iniset $CONF_FILE DEFAULT protocol_dir $CONF_DIR/protocols
iniset $CONF_FILE DEFAULT protocol_dir "$CONF_DIR/protocols"
iniset $CONF_FILE DEFAULT keystone_url "$OS_AUTH_URL"
iniset $CONF_FILE database connection "$CONNECTION"
iniset $CONF_FILE keystone_authtoken signing_dir $SIGNING_DIR
iniset $CONF_FILE keystone_authtoken auth_host $AUTH_HOST
iniset $CONF_FILE keystone_authtoken admin_user $SERVICE_USERNAME
iniset $CONF_FILE keystone_authtoken admin_password $SERVICE_PASSWORD
iniset $CONF_FILE keystone_authtoken admin_tenant_name $SERVICE_TENANT
iniset $CONF_FILE keystone_authtoken auth_protocol $AUTH_PROTO
iniset $CONF_FILE keystone_authtoken auth_port $AUTH_PORT
iniset $CONF_FILE keystone_authtoken identity_uri "$OS_AUTH_URL"
#init cache dir