Blackify everything else

Black used with the '-l 79 -S' flags.

A future change will ignore this commit in git-blame history by adding a
'git-blame-ignore-revs' file.

Change-Id: Ie106a5cec8831e113a2b764b62b712a205e3153b
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane 2023-05-05 11:25:50 +01:00
parent 004c7352d0
commit a36f514295
84 changed files with 4465 additions and 2838 deletions

View File

@ -77,10 +77,13 @@ htmlhelp_basename = 'openstacksdkdoc'
# (source start file, target name, title, author, documentclass # (source start file, target name, title, author, documentclass
# [howto/manual]). # [howto/manual]).
latex_documents = [ latex_documents = [
('index', (
'doc-openstacksdk.tex', 'index',
'OpenStackSDK Documentation', 'doc-openstacksdk.tex',
'OpenStack Foundation', 'manual'), 'OpenStackSDK Documentation',
'OpenStack Foundation',
'manual',
),
] ]
# Allow deeper levels of nesting for \begin...\end stanzas # Allow deeper levels of nesting for \begin...\end stanzas

View File

@ -16,11 +16,11 @@ from openstack import cloud as openstack
openstack.enable_logging(debug=True) openstack.enable_logging(debug=True)
for cloud_name, region_name in [ for cloud_name, region_name in [
('my-vexxhost', 'ca-ymq-1'), ('my-vexxhost', 'ca-ymq-1'),
('my-citycloud', 'Buf1'), ('my-citycloud', 'Buf1'),
('my-internap', 'ams01')]: ('my-internap', 'ams01'),
]:
# Initialize cloud # Initialize cloud
cloud = openstack.connect( cloud = openstack.connect(cloud=cloud_name, region_name=region_name)
cloud=cloud_name, region_name=region_name)
for server in cloud.search_servers('my-server'): for server in cloud.search_servers('my-server'):
cloud.delete_server(server, wait=True, delete_ips=True) cloud.delete_server(server, wait=True, delete_ips=True)

View File

@ -16,20 +16,31 @@ from openstack import cloud as openstack
openstack.enable_logging(debug=True) openstack.enable_logging(debug=True)
for cloud_name, region_name, image, flavor_id in [ for cloud_name, region_name, image, flavor_id in [
('my-vexxhost', 'ca-ymq-1', 'Ubuntu 16.04.1 LTS [2017-03-03]', (
'5cf64088-893b-46b5-9bb1-ee020277635d'), 'my-vexxhost',
('my-citycloud', 'Buf1', 'Ubuntu 16.04 Xenial Xerus', 'ca-ymq-1',
'0dab10b5-42a2-438e-be7b-505741a7ffcc'), 'Ubuntu 16.04.1 LTS [2017-03-03]',
('my-internap', 'ams01', 'Ubuntu 16.04 LTS (Xenial Xerus)', '5cf64088-893b-46b5-9bb1-ee020277635d',
'A1.4')]: ),
(
'my-citycloud',
'Buf1',
'Ubuntu 16.04 Xenial Xerus',
'0dab10b5-42a2-438e-be7b-505741a7ffcc',
),
('my-internap', 'ams01', 'Ubuntu 16.04 LTS (Xenial Xerus)', 'A1.4'),
]:
# Initialize cloud # Initialize cloud
cloud = openstack.connect( cloud = openstack.connect(cloud=cloud_name, region_name=region_name)
cloud=cloud_name, region_name=region_name)
# Boot a server, wait for it to boot, and then do whatever is needed # Boot a server, wait for it to boot, and then do whatever is needed
# to get a public ip for it. # to get a public ip for it.
server = cloud.create_server( server = cloud.create_server(
'my-server', image=image, flavor=dict(id=flavor_id), 'my-server',
wait=True, auto_ip=True) image=image,
flavor=dict(id=flavor_id),
wait=True,
auto_ip=True,
)
# Delete it - this is a demo # Delete it - this is a demo
cloud.delete_server(server, wait=True, delete_ips=True) cloud.delete_server(server, wait=True, delete_ips=True)

View File

@ -16,21 +16,24 @@ from openstack import cloud as openstack
openstack.enable_logging(debug=True) openstack.enable_logging(debug=True)
for cloud_name, region_name, image, flavor in [ for cloud_name, region_name, image, flavor in [
('my-vexxhost', 'ca-ymq-1', (
'Ubuntu 16.04.1 LTS [2017-03-03]', 'v1-standard-4'), 'my-vexxhost',
('my-citycloud', 'Buf1', 'ca-ymq-1',
'Ubuntu 16.04 Xenial Xerus', '4C-4GB-100GB'), 'Ubuntu 16.04.1 LTS [2017-03-03]',
('my-internap', 'ams01', 'v1-standard-4',
'Ubuntu 16.04 LTS (Xenial Xerus)', 'A1.4')]: ),
('my-citycloud', 'Buf1', 'Ubuntu 16.04 Xenial Xerus', '4C-4GB-100GB'),
('my-internap', 'ams01', 'Ubuntu 16.04 LTS (Xenial Xerus)', 'A1.4'),
]:
# Initialize cloud # Initialize cloud
cloud = openstack.connect( cloud = openstack.connect(cloud=cloud_name, region_name=region_name)
cloud=cloud_name, region_name=region_name)
cloud.delete_server('my-server', wait=True, delete_ips=True) cloud.delete_server('my-server', wait=True, delete_ips=True)
# Boot a server, wait for it to boot, and then do whatever is needed # Boot a server, wait for it to boot, and then do whatever is needed
# to get a public ip for it. # to get a public ip for it.
server = cloud.create_server( server = cloud.create_server(
'my-server', image=image, flavor=flavor, wait=True, auto_ip=True) 'my-server', image=image, flavor=flavor, wait=True, auto_ip=True
)
print(server.name) print(server.name)
print(server['name']) print(server['name'])
cloud.pprint(server) cloud.pprint(server)

View File

@ -11,8 +11,8 @@
# under the License. # under the License.
from openstack import cloud as openstack from openstack import cloud as openstack
openstack.enable_logging(debug=True) openstack.enable_logging(debug=True)
cloud = openstack.connect( cloud = openstack.connect(cloud='my-vexxhost', region_name='ca-ymq-1')
cloud='my-vexxhost', region_name='ca-ymq-1')
cloud.get_image('Ubuntu 16.04.1 LTS [2017-03-03]') cloud.get_image('Ubuntu 16.04.1 LTS [2017-03-03]')

View File

@ -11,9 +11,10 @@
# under the License. # under the License.
from openstack import cloud as openstack from openstack import cloud as openstack
openstack.enable_logging() openstack.enable_logging()
cloud = openstack.connect(cloud='fuga', region_name='cystack') cloud = openstack.connect(cloud='fuga', region_name='cystack')
cloud.pprint([ cloud.pprint(
image for image in cloud.list_images() [image for image in cloud.list_images() if 'ubuntu' in image.name.lower()]
if 'ubuntu' in image.name.lower()]) )

View File

@ -11,8 +11,8 @@
# under the License. # under the License.
from openstack import cloud as openstack from openstack import cloud as openstack
openstack.enable_logging(http_debug=True) openstack.enable_logging(http_debug=True)
cloud = openstack.connect( cloud = openstack.connect(cloud='my-vexxhost', region_name='ca-ymq-1')
cloud='my-vexxhost', region_name='ca-ymq-1')
cloud.get_image('Ubuntu 16.04.1 LTS [2017-03-03]') cloud.get_image('Ubuntu 16.04.1 LTS [2017-03-03]')

View File

@ -11,6 +11,7 @@
# under the License. # under the License.
from openstack import cloud as openstack from openstack import cloud as openstack
openstack.enable_logging(debug=True) openstack.enable_logging(debug=True)
cloud = openstack.connect(cloud='ovh', region_name='SBG1') cloud = openstack.connect(cloud='ovh', region_name='SBG1')

View File

@ -11,9 +11,11 @@
# under the License. # under the License.
from openstack import cloud as openstack from openstack import cloud as openstack
openstack.enable_logging() openstack.enable_logging()
cloud = openstack.connect(cloud='fuga', region_name='cystack') cloud = openstack.connect(cloud='fuga', region_name='cystack')
image = cloud.get_image( image = cloud.get_image(
'Ubuntu 16.04 LTS - Xenial Xerus - 64-bit - Fuga Cloud Based Image') 'Ubuntu 16.04 LTS - Xenial Xerus - 64-bit - Fuga Cloud Based Image'
)
cloud.pprint(image) cloud.pprint(image)

View File

@ -11,14 +11,18 @@
# under the License. # under the License.
import openstack import openstack
openstack.enable_logging(debug=True) openstack.enable_logging(debug=True)
cloud = openstack.connect(cloud='my-citycloud', region_name='Buf1') cloud = openstack.connect(cloud='my-citycloud', region_name='Buf1')
try: try:
server = cloud.create_server( server = cloud.create_server(
'my-server', image='Ubuntu 16.04 Xenial Xerus', 'my-server',
image='Ubuntu 16.04 Xenial Xerus',
flavor=dict(id='0dab10b5-42a2-438e-be7b-505741a7ffcc'), flavor=dict(id='0dab10b5-42a2-438e-be7b-505741a7ffcc'),
wait=True, auto_ip=True) wait=True,
auto_ip=True,
)
print("\n\nFull Server\n\n") print("\n\nFull Server\n\n")
cloud.pprint(server) cloud.pprint(server)

View File

@ -11,6 +11,7 @@
# under the License. # under the License.
import openstack import openstack
openstack.enable_logging(debug=True) openstack.enable_logging(debug=True)
cloud = openstack.connect(cloud='rax', region_name='DFW') cloud = openstack.connect(cloud='rax', region_name='DFW')

View File

@ -11,6 +11,7 @@
# under the License. # under the License.
import openstack import openstack
openstack.enable_logging(debug=True) openstack.enable_logging(debug=True)
cloud = openstack.connect(cloud='kiss', region_name='region1') cloud = openstack.connect(cloud='kiss', region_name='region1')

View File

@ -11,10 +11,11 @@
# under the License. # under the License.
import openstack import openstack
openstack.enable_logging() openstack.enable_logging()
cloud = openstack.connect( cloud = openstack.connect(cloud='fuga', region_name='cystack', strict=True)
cloud='fuga', region_name='cystack', strict=True)
image = cloud.get_image( image = cloud.get_image(
'Ubuntu 16.04 LTS - Xenial Xerus - 64-bit - Fuga Cloud Based Image') 'Ubuntu 16.04 LTS - Xenial Xerus - 64-bit - Fuga Cloud Based Image'
)
cloud.pprint(image) cloud.pprint(image)

View File

@ -11,12 +11,15 @@
# under the License. # under the License.
import openstack import openstack
openstack.enable_logging(debug=True) openstack.enable_logging(debug=True)
cloud = openstack.connect(cloud='ovh', region_name='SBG1') cloud = openstack.connect(cloud='ovh', region_name='SBG1')
cloud.create_object( cloud.create_object(
container='my-container', name='my-object', container='my-container',
name='my-object',
filename='/home/mordred/briarcliff.sh3d', filename='/home/mordred/briarcliff.sh3d',
segment_size=1000000) segment_size=1000000,
)
cloud.delete_object('my-container', 'my-object') cloud.delete_object('my-container', 'my-object')
cloud.delete_container('my-container') cloud.delete_container('my-container')

View File

@ -11,12 +11,15 @@
# under the License. # under the License.
import openstack import openstack
openstack.enable_logging(debug=True) openstack.enable_logging(debug=True)
cloud = openstack.connect(cloud='ovh', region_name='SBG1') cloud = openstack.connect(cloud='ovh', region_name='SBG1')
cloud.create_object( cloud.create_object(
container='my-container', name='my-object', container='my-container',
name='my-object',
filename='/home/mordred/briarcliff.sh3d', filename='/home/mordred/briarcliff.sh3d',
segment_size=1000000) segment_size=1000000,
)
cloud.delete_object('my-container', 'my-object') cloud.delete_object('my-container', 'my-object')
cloud.delete_container('my-container') cloud.delete_container('my-container')

View File

@ -11,8 +11,10 @@
# under the License. # under the License.
import openstack import openstack
openstack.enable_logging(http_debug=True) openstack.enable_logging(http_debug=True)
cloud = openstack.connect( cloud = openstack.connect(
cloud='datacentred', app_name='AmazingApp', app_version='1.0') cloud='datacentred', app_name='AmazingApp', app_version='1.0'
)
cloud.list_networks() cloud.list_networks()

View File

@ -107,9 +107,7 @@ def replace_nodes_in_cluster(conn):
old_node = NODE_ID old_node = NODE_ID
new_node = "cd803d4a-015d-4223-b15f-db29bad3146c" new_node = "cd803d4a-015d-4223-b15f-db29bad3146c"
spec = { spec = {old_node: new_node}
old_node: new_node
}
res = conn.clustering.replace_nodes_in_cluster(CLUSTER_ID, **spec) res = conn.clustering.replace_nodes_in_cluster(CLUSTER_ID, **spec)
print(res) print(res)
@ -135,7 +133,7 @@ def resize_cluster(conn):
'min_size': 1, 'min_size': 1,
'max_size': 6, 'max_size': 6,
'adjustment_type': 'EXACT_CAPACITY', 'adjustment_type': 'EXACT_CAPACITY',
'number': 2 'number': 2,
} }
res = conn.clustering.resize_cluster(CLUSTER_ID, **spec) res = conn.clustering.resize_cluster(CLUSTER_ID, **spec)
print(res) print(res)
@ -146,7 +144,8 @@ def attach_policy_to_cluster(conn):
spec = {'enabled': True} spec = {'enabled': True}
res = conn.clustering.attach_policy_to_cluster( res = conn.clustering.attach_policy_to_cluster(
CLUSTER_ID, POLICY_ID, **spec) CLUSTER_ID, POLICY_ID, **spec
)
print(res) print(res)

View File

@ -38,8 +38,8 @@ def create_policy(conn):
'properties': { 'properties': {
'criteria': 'oldest_first', 'criteria': 'oldest_first',
'destroy_after_deletion': True, 'destroy_after_deletion': True,
} },
} },
} }
policy = conn.clustering.create_policy(attrs) policy = conn.clustering.create_policy(attrs)

View File

@ -44,10 +44,8 @@ def create_profile(conn):
'name': SERVER_NAME, 'name': SERVER_NAME,
'flavor': FLAVOR_NAME, 'flavor': FLAVOR_NAME,
'image': IMAGE_NAME, 'image': IMAGE_NAME,
'networks': { 'networks': {'network': NETWORK_NAME},
'network': NETWORK_NAME },
}
}
} }
profile = conn.clustering.create_profile(spec) profile = conn.clustering.create_profile(spec)

View File

@ -39,10 +39,8 @@ def create_receiver(conn):
"action": "CLUSTER_SCALE_OUT", "action": "CLUSTER_SCALE_OUT",
"cluster_id": CLUSTER_ID, "cluster_id": CLUSTER_ID,
"name": FAKE_NAME, "name": FAKE_NAME,
"params": { "params": {"count": "1"},
"count": "1" "type": "webhook",
},
"type": "webhook"
} }
receiver = conn.clustering.create_receiver(**spec) receiver = conn.clustering.create_receiver(**spec)
@ -66,12 +64,7 @@ def find_receiver(conn):
def update_receiver(conn): def update_receiver(conn):
print("Update Receiver:") print("Update Receiver:")
spec = { spec = {"name": "test_receiver2", "params": {"count": "2"}}
"name": "test_receiver2",
"params": {
"count": "2"
}
}
receiver = conn.clustering.update_receiver(FAKE_NAME, **spec) receiver = conn.clustering.update_receiver(FAKE_NAME, **spec)
print(receiver.to_dict()) print(receiver.to_dict())

View File

@ -62,11 +62,17 @@ def create_server(conn):
keypair = create_keypair(conn) keypair = create_keypair(conn)
server = conn.compute.create_server( server = conn.compute.create_server(
name=SERVER_NAME, image_id=image.id, flavor_id=flavor.id, name=SERVER_NAME,
networks=[{"uuid": network.id}], key_name=keypair.name) image_id=image.id,
flavor_id=flavor.id,
networks=[{"uuid": network.id}],
key_name=keypair.name,
)
server = conn.compute.wait_for_server(server) server = conn.compute.wait_for_server(server)
print("ssh -i {key} root@{ip}".format( print(
key=PRIVATE_KEYPAIR_FILE, "ssh -i {key} root@{ip}".format(
ip=server.access_ipv4)) key=PRIVATE_KEYPAIR_FILE, ip=server.access_ipv4
)
)

View File

@ -45,8 +45,9 @@ class Opts:
def _get_resource_value(resource_key, default): def _get_resource_value(resource_key, default):
return config.get_extra_config( return config.get_extra_config(EXAMPLE_CONFIG_KEY).get(
EXAMPLE_CONFIG_KEY).get(resource_key, default) resource_key, default
)
SERVER_NAME = 'openstacksdk-example' SERVER_NAME = 'openstacksdk-example'
@ -55,10 +56,12 @@ FLAVOR_NAME = _get_resource_value('flavor_name', 'm1.small')
NETWORK_NAME = _get_resource_value('network_name', 'private') NETWORK_NAME = _get_resource_value('network_name', 'private')
KEYPAIR_NAME = _get_resource_value('keypair_name', 'openstacksdk-example') KEYPAIR_NAME = _get_resource_value('keypair_name', 'openstacksdk-example')
SSH_DIR = _get_resource_value( SSH_DIR = _get_resource_value(
'ssh_dir', '{home}/.ssh'.format(home=os.path.expanduser("~"))) 'ssh_dir', '{home}/.ssh'.format(home=os.path.expanduser("~"))
)
PRIVATE_KEYPAIR_FILE = _get_resource_value( PRIVATE_KEYPAIR_FILE = _get_resource_value(
'private_keypair_file', '{ssh_dir}/id_rsa.{key}'.format( 'private_keypair_file',
ssh_dir=SSH_DIR, key=KEYPAIR_NAME)) '{ssh_dir}/id_rsa.{key}'.format(ssh_dir=SSH_DIR, key=KEYPAIR_NAME),
)
EXAMPLE_IMAGE_NAME = 'openstacksdk-example-public-image' EXAMPLE_IMAGE_NAME = 'openstacksdk-example-public-image'
@ -72,8 +75,15 @@ def create_connection_from_args():
return openstack.connect(options=parser) return openstack.connect(options=parser)
def create_connection(auth_url, region, project_name, username, password, def create_connection(
user_domain, project_domain): auth_url,
region,
project_name,
username,
password,
user_domain,
project_domain,
):
return openstack.connect( return openstack.connect(
auth_url=auth_url, auth_url=auth_url,
project_name=project_name, project_name=project_name,

View File

@ -24,8 +24,10 @@ def import_image(conn):
print("Import Image:") print("Import Image:")
# Url where glance can download the image # Url where glance can download the image
uri = 'https://download.cirros-cloud.net/0.4.0/' \ uri = (
'cirros-0.4.0-x86_64-disk.img' 'https://download.cirros-cloud.net/0.4.0/'
'cirros-0.4.0-x86_64-disk.img'
)
# Build the image attributes and import the image. # Build the image attributes and import the image.
image_attrs = { image_attrs = {

View File

@ -18,8 +18,10 @@ List resources from the Key Manager service.
def create_secret(conn): def create_secret(conn):
print("Create a secret:") print("Create a secret:")
conn.key_manager.create_secret(name="My public key", conn.key_manager.create_secret(
secret_type="public", name="My public key",
expiration="2020-02-28T23:59:59", secret_type="public",
payload="ssh rsa...", expiration="2020-02-28T23:59:59",
payload_content_type="text/plain") payload="ssh rsa...",
payload_content_type="text/plain",
)

View File

@ -26,6 +26,6 @@ def list_secrets_query(conn):
print("List Secrets:") print("List Secrets:")
for secret in conn.key_manager.secrets( for secret in conn.key_manager.secrets(
secret_type="symmetric", secret_type="symmetric", expiration="gte:2020-01-01T00:00:00"
expiration="gte:2020-01-01T00:00:00"): ):
print(secret) print(secret)

View File

@ -22,7 +22,8 @@ def create_network(conn):
print("Create Network:") print("Create Network:")
example_network = conn.network.create_network( example_network = conn.network.create_network(
name='openstacksdk-example-project-network') name='openstacksdk-example-project-network'
)
print(example_network) print(example_network)
@ -31,6 +32,7 @@ def create_network(conn):
network_id=example_network.id, network_id=example_network.id,
ip_version='4', ip_version='4',
cidr='10.0.2.0/24', cidr='10.0.2.0/24',
gateway_ip='10.0.2.1') gateway_ip='10.0.2.1',
)
print(example_subnet) print(example_subnet)

View File

@ -22,7 +22,8 @@ def delete_network(conn):
print("Delete Network:") print("Delete Network:")
example_network = conn.network.find_network( example_network = conn.network.find_network(
'openstacksdk-example-project-network') 'openstacksdk-example-project-network'
)
for example_subnet in example_network.subnet_ids: for example_subnet in example_network.subnet_ids:
conn.network.delete_subnet(example_subnet, ignore_missing=False) conn.network.delete_subnet(example_subnet, ignore_missing=False)

View File

@ -22,7 +22,8 @@ def open_port(conn):
print("Open a port:") print("Open a port:")
example_sec_group = conn.network.create_security_group( example_sec_group = conn.network.create_security_group(
name='openstacksdk-example-security-group') name='openstacksdk-example-security-group'
)
print(example_sec_group) print(example_sec_group)
@ -33,7 +34,8 @@ def open_port(conn):
protocol='HTTPS', protocol='HTTPS',
port_range_max='443', port_range_max='443',
port_range_min='443', port_range_min='443',
ethertype='IPv4') ethertype='IPv4',
)
print(example_rule) print(example_rule)
@ -42,7 +44,8 @@ def allow_ping(conn):
print("Allow pings:") print("Allow pings:")
example_sec_group = conn.network.create_security_group( example_sec_group = conn.network.create_security_group(
name='openstacksdk-example-security-group2') name='openstacksdk-example-security-group2'
)
print(example_sec_group) print(example_sec_group)
@ -53,6 +56,7 @@ def allow_ping(conn):
protocol='icmp', protocol='icmp',
port_range_max=None, port_range_max=None,
port_range_min=None, port_range_min=None,
ethertype='IPv4') ethertype='IPv4',
)
print(example_rule) print(example_rule)

View File

@ -31,8 +31,10 @@ def get_share_instance(conn, share_instance_id):
def reset_share_instance_status(conn, share_instance_id, status): def reset_share_instance_status(conn, share_instance_id, status):
print('Reset the status of the share instance with the given ' print(
'share_instance_id to the given status') 'Reset the status of the share instance with the given '
'share_instance_id to the given status'
)
conn.share.reset_share_instance_status(share_instance_id, status) conn.share.reset_share_instance_status(share_instance_id, status)

View File

@ -19,16 +19,18 @@ import pbr.version
def show_version(args): def show_version(args):
print("OpenstackSDK Version %s" % print(
pbr.version.VersionInfo('openstacksdk').version_string_with_vcs()) "OpenstackSDK Version %s"
% pbr.version.VersionInfo('openstacksdk').version_string_with_vcs()
)
parser = argparse.ArgumentParser(description="Openstack SDK") parser = argparse.ArgumentParser(description="Openstack SDK")
subparsers = parser.add_subparsers(title='commands', subparsers = parser.add_subparsers(title='commands', dest='command')
dest='command')
cmd_version = subparsers.add_parser('version', cmd_version = subparsers.add_parser(
help='show Openstack SDK version') 'version', help='show Openstack SDK version'
)
cmd_version.set_defaults(func=show_version) cmd_version.set_defaults(func=show_version)
args = parser.parse_args() args = parser.parse_args()

View File

@ -44,7 +44,10 @@ def setup_logging(name, handlers=None, level=None):
def enable_logging( def enable_logging(
debug=False, http_debug=False, path=None, stream=None, debug=False,
http_debug=False,
path=None,
stream=None,
format_stream=False, format_stream=False,
format_template='%(asctime)s %(levelname)s: %(name)s %(message)s', format_template='%(asctime)s %(levelname)s: %(name)s %(message)s',
handlers=None, handlers=None,
@ -121,9 +124,11 @@ def enable_logging(
# enable_logging should not be used and instead python logging should # enable_logging should not be used and instead python logging should
# be configured directly. # be configured directly.
setup_logging( setup_logging(
'urllib3', handlers=[logging.NullHandler()], level=logging.INFO) 'urllib3', handlers=[logging.NullHandler()], level=logging.INFO
)
setup_logging( setup_logging(
'stevedore', handlers=[logging.NullHandler()], level=logging.INFO) 'stevedore', handlers=[logging.NullHandler()], level=logging.INFO
)
# Suppress warning about keystoneauth loggers # Suppress warning about keystoneauth loggers
setup_logging('keystoneauth.discovery') setup_logging('keystoneauth.discovery')
setup_logging('keystoneauth.identity.base') setup_logging('keystoneauth.identity.base')

View File

@ -6,7 +6,9 @@ from openstack.baremetal_introspection import baremetal_introspection_service
from openstack.block_storage import block_storage_service from openstack.block_storage import block_storage_service
from openstack.clustering import clustering_service from openstack.clustering import clustering_service
from openstack.compute import compute_service from openstack.compute import compute_service
from openstack.container_infrastructure_management import container_infrastructure_management_service from openstack.container_infrastructure_management import (
container_infrastructure_management_service,
)
from openstack.database import database_service from openstack.database import database_service
from openstack.dns import dns_service from openstack.dns import dns_service
from openstack.identity import identity_service from openstack.identity import identity_service
@ -31,32 +33,52 @@ class ServicesMixin:
image = image_service.ImageService(service_type='image') image = image_service.ImageService(service_type='image')
load_balancer = load_balancer_service.LoadBalancerService(service_type='load-balancer') load_balancer = load_balancer_service.LoadBalancerService(
service_type='load-balancer'
)
object_store = object_store_service.ObjectStoreService(service_type='object-store') object_store = object_store_service.ObjectStoreService(
service_type='object-store'
)
clustering = clustering_service.ClusteringService(service_type='clustering') clustering = clustering_service.ClusteringService(
service_type='clustering'
)
resource_cluster = clustering resource_cluster = clustering
cluster = clustering cluster = clustering
data_processing = service_description.ServiceDescription(service_type='data-processing') data_processing = service_description.ServiceDescription(
service_type='data-processing'
)
baremetal = baremetal_service.BaremetalService(service_type='baremetal') baremetal = baremetal_service.BaremetalService(service_type='baremetal')
bare_metal = baremetal bare_metal = baremetal
baremetal_introspection = baremetal_introspection_service.BaremetalIntrospectionService(service_type='baremetal-introspection') baremetal_introspection = (
baremetal_introspection_service.BaremetalIntrospectionService(
service_type='baremetal-introspection'
)
)
key_manager = key_manager_service.KeyManagerService(service_type='key-manager') key_manager = key_manager_service.KeyManagerService(
service_type='key-manager'
)
resource_optimization = service_description.ServiceDescription(service_type='resource-optimization') resource_optimization = service_description.ServiceDescription(
service_type='resource-optimization'
)
infra_optim = resource_optimization infra_optim = resource_optimization
message = message_service.MessageService(service_type='message') message = message_service.MessageService(service_type='message')
messaging = message messaging = message
application_catalog = service_description.ServiceDescription(service_type='application-catalog') application_catalog = service_description.ServiceDescription(
service_type='application-catalog'
)
container_infrastructure_management = container_infrastructure_management_service.ContainerInfrastructureManagementService(service_type='container-infrastructure-management') container_infrastructure_management = container_infrastructure_management_service.ContainerInfrastructureManagementService(
service_type='container-infrastructure-management'
)
container_infra = container_infrastructure_management container_infra = container_infrastructure_management
container_infrastructure = container_infrastructure_management container_infrastructure = container_infrastructure_management
@ -68,17 +90,27 @@ class ServicesMixin:
rating = service_description.ServiceDescription(service_type='rating') rating = service_description.ServiceDescription(service_type='rating')
operator_policy = service_description.ServiceDescription(service_type='operator-policy') operator_policy = service_description.ServiceDescription(
service_type='operator-policy'
)
policy = operator_policy policy = operator_policy
shared_file_system = shared_file_system_service.SharedFilesystemService(service_type='shared-file-system') shared_file_system = shared_file_system_service.SharedFilesystemService(
service_type='shared-file-system'
)
share = shared_file_system share = shared_file_system
data_protection_orchestration = service_description.ServiceDescription(service_type='data-protection-orchestration') data_protection_orchestration = service_description.ServiceDescription(
service_type='data-protection-orchestration'
)
orchestration = orchestration_service.OrchestrationService(service_type='orchestration') orchestration = orchestration_service.OrchestrationService(
service_type='orchestration'
)
block_storage = block_storage_service.BlockStorageService(service_type='block-storage') block_storage = block_storage_service.BlockStorageService(
service_type='block-storage'
)
block_store = block_storage block_store = block_storage
volume = block_storage volume = block_storage
@ -92,44 +124,69 @@ class ServicesMixin:
event = service_description.ServiceDescription(service_type='event') event = service_description.ServiceDescription(service_type='event')
events = event events = event
application_deployment = service_description.ServiceDescription(service_type='application-deployment') application_deployment = service_description.ServiceDescription(
service_type='application-deployment'
)
application_deployment = application_deployment application_deployment = application_deployment
multi_region_network_automation = service_description.ServiceDescription(service_type='multi-region-network-automation') multi_region_network_automation = service_description.ServiceDescription(
service_type='multi-region-network-automation'
)
tricircle = multi_region_network_automation tricircle = multi_region_network_automation
database = database_service.DatabaseService(service_type='database') database = database_service.DatabaseService(service_type='database')
application_container = service_description.ServiceDescription(service_type='application-container') application_container = service_description.ServiceDescription(
service_type='application-container'
)
container = application_container container = application_container
root_cause_analysis = service_description.ServiceDescription(service_type='root-cause-analysis') root_cause_analysis = service_description.ServiceDescription(
service_type='root-cause-analysis'
)
rca = root_cause_analysis rca = root_cause_analysis
nfv_orchestration = service_description.ServiceDescription(service_type='nfv-orchestration') nfv_orchestration = service_description.ServiceDescription(
service_type='nfv-orchestration'
)
network = network_service.NetworkService(service_type='network') network = network_service.NetworkService(service_type='network')
backup = service_description.ServiceDescription(service_type='backup') backup = service_description.ServiceDescription(service_type='backup')
monitoring_logging = service_description.ServiceDescription(service_type='monitoring-logging') monitoring_logging = service_description.ServiceDescription(
service_type='monitoring-logging'
)
monitoring_log_api = monitoring_logging monitoring_log_api = monitoring_logging
monitoring = service_description.ServiceDescription(service_type='monitoring') monitoring = service_description.ServiceDescription(
service_type='monitoring'
)
monitoring_events = service_description.ServiceDescription(service_type='monitoring-events') monitoring_events = service_description.ServiceDescription(
service_type='monitoring-events'
)
placement = placement_service.PlacementService(service_type='placement') placement = placement_service.PlacementService(service_type='placement')
instance_ha = instance_ha_service.InstanceHaService(service_type='instance-ha') instance_ha = instance_ha_service.InstanceHaService(
service_type='instance-ha'
)
ha = instance_ha ha = instance_ha
reservation = service_description.ServiceDescription(service_type='reservation') reservation = service_description.ServiceDescription(
service_type='reservation'
)
function_engine = service_description.ServiceDescription(service_type='function-engine') function_engine = service_description.ServiceDescription(
service_type='function-engine'
)
accelerator = accelerator_service.AcceleratorService(service_type='accelerator') accelerator = accelerator_service.AcceleratorService(
service_type='accelerator'
)
admin_logic = service_description.ServiceDescription(service_type='admin-logic') admin_logic = service_description.ServiceDescription(
service_type='admin-logic'
)
registration = admin_logic registration = admin_logic

View File

@ -82,14 +82,13 @@ class MetadataMixin:
url = utils.urljoin(self.base_path, self.id, 'metadata', key) url = utils.urljoin(self.base_path, self.id, 'metadata', key)
response = session.get(url) response = session.get(url)
exceptions.raise_from_response( exceptions.raise_from_response(
response, error_message='Metadata item does not exist') response, error_message='Metadata item does not exist'
)
meta = response.json().get('meta', {}) meta = response.json().get('meta', {})
# Here we need to potentially init metadata # Here we need to potentially init metadata
metadata = self.metadata or {} metadata = self.metadata or {}
metadata[key] = meta.get(key) metadata[key] = meta.get(key)
self._body.attributes.update({ self._body.attributes.update({'metadata': metadata})
'metadata': metadata
})
return self return self
@ -101,17 +100,12 @@ class MetadataMixin:
:param str value: The value. :param str value: The value.
""" """
url = utils.urljoin(self.base_path, self.id, 'metadata', key) url = utils.urljoin(self.base_path, self.id, 'metadata', key)
response = session.put( response = session.put(url, json={'meta': {key: value}})
url,
json={'meta': {key: value}}
)
exceptions.raise_from_response(response) exceptions.raise_from_response(response)
# we do not want to update tags directly # we do not want to update tags directly
metadata = self.metadata metadata = self.metadata
metadata[key] = value metadata[key] = value
self._body.attributes.update({ self._body.attributes.update({'metadata': metadata})
'metadata': metadata
})
return self return self
def delete_metadata_item(self, session, key): def delete_metadata_item(self, session, key):
@ -132,7 +126,5 @@ class MetadataMixin:
metadata = {} metadata = {}
except ValueError: except ValueError:
pass # do nothing! pass # do nothing!
self._body.attributes.update({ self._body.attributes.update({'metadata': metadata})
'metadata': metadata
})
return self return self

View File

@ -26,8 +26,7 @@ class QuotaSet(resource.Resource):
allow_delete = True allow_delete = True
allow_commit = True allow_commit = True
_query_mapping = resource.QueryParameters( _query_mapping = resource.QueryParameters("usage")
"usage")
# NOTE(gtema) Sadly this attribute is useless in all the methods, but keep # NOTE(gtema) Sadly this attribute is useless in all the methods, but keep
# it here extra as a reminder # it here extra as a reminder
@ -47,8 +46,14 @@ class QuotaSet(resource.Resource):
project_id = resource.URI('project_id') project_id = resource.URI('project_id')
def fetch(self, session, requires_id=False, def fetch(
base_path=None, error_message=None, **params): self,
session,
requires_id=False,
base_path=None,
error_message=None,
**params
):
return super(QuotaSet, self).fetch( return super(QuotaSet, self).fetch(
session, session,
requires_id=False, requires_id=False,
@ -93,8 +98,9 @@ class QuotaSet(resource.Resource):
if 'in_use' in val: if 'in_use' in val:
normalized_attrs['usage'][key] = val['in_use'] normalized_attrs['usage'][key] = val['in_use']
if 'reserved' in val: if 'reserved' in val:
normalized_attrs['reservation'][key] = \ normalized_attrs['reservation'][key] = val[
val['reserved'] 'reserved'
]
if 'limit' in val: if 'limit' in val:
normalized_attrs[key] = val['limit'] normalized_attrs[key] = val['limit']
else: else:

View File

@ -81,8 +81,9 @@ class TagMixin:
url = utils.urljoin(self.base_path, self.id, 'tags', tag) url = utils.urljoin(self.base_path, self.id, 'tags', tag)
session = self._get_session(session) session = self._get_session(session)
response = session.get(url) response = session.get(url)
exceptions.raise_from_response(response, exceptions.raise_from_response(
error_message='Tag does not exist') response, error_message='Tag does not exist'
)
return self return self
def add_tag(self, session, tag): def add_tag(self, session, tag):
@ -98,9 +99,7 @@ class TagMixin:
# we do not want to update tags directly # we do not want to update tags directly
tags = self.tags tags = self.tags
tags.append(tag) tags.append(tag)
self._body.attributes.update({ self._body.attributes.update({'tags': tags})
'tags': tags
})
return self return self
def remove_tag(self, session, tag): def remove_tag(self, session, tag):
@ -121,7 +120,5 @@ class TagMixin:
tags.remove(tag) tags.remove(tag)
except ValueError: except ValueError:
pass # do nothing! pass # do nothing!
self._body.attributes.update({ self._body.attributes.update({'tags': tags})
'tags': tags
})
return self return self

View File

@ -18,15 +18,20 @@ from openstack.config.loader import OpenStackConfig # noqa
def get_cloud_region( def get_cloud_region(
service_key=None, options=None, service_key=None,
app_name=None, app_version=None, options=None,
load_yaml_config=True, app_name=None,
load_envvars=True, app_version=None,
**kwargs): load_yaml_config=True,
load_envvars=True,
**kwargs
):
config = OpenStackConfig( config = OpenStackConfig(
load_yaml_config=load_yaml_config, load_yaml_config=load_yaml_config,
load_envvars=load_envvars, load_envvars=load_envvars,
app_name=app_name, app_version=app_version) app_name=app_name,
app_version=app_version,
)
if options: if options:
config.register_argparse_arguments(options, sys.argv, service_key) config.register_argparse_arguments(options, sys.argv, service_key)
parsed_options = options.parse_known_args(sys.argv) parsed_options = options.parse_known_args(sys.argv)

View File

@ -22,7 +22,9 @@ def normalize_keys(config):
elif isinstance(value, bool): elif isinstance(value, bool):
new_config[key] = value new_config[key] = value
elif isinstance(value, int) and key not in ( elif isinstance(value, int) and key not in (
'verbose_level', 'api_timeout'): 'verbose_level',
'api_timeout',
):
new_config[key] = str(value) new_config[key] = str(value)
elif isinstance(value, float): elif isinstance(value, float):
new_config[key] = str(value) new_config[key] = str(value)

View File

@ -18,7 +18,6 @@ from openstack.config import cloud_region
class CloudConfig(cloud_region.CloudRegion): class CloudConfig(cloud_region.CloudRegion):
def __init__(self, name, region, config, **kwargs): def __init__(self, name, region, config, **kwargs):
super(CloudConfig, self).__init__(name, region, config, **kwargs) super(CloudConfig, self).__init__(name, region, config, **kwargs)
self.region = region self.region = region

View File

@ -28,6 +28,7 @@ from keystoneauth1.loading import adapter as ks_load_adap
from keystoneauth1 import session as ks_session from keystoneauth1 import session as ks_session
import os_service_types import os_service_types
import requestsexceptions import requestsexceptions
try: try:
import statsd import statsd
except ImportError: except ImportError:
@ -52,9 +53,11 @@ from openstack import version as openstack_version
_logger = _log.setup_logging('openstack') _logger = _log.setup_logging('openstack')
SCOPE_KEYS = { SCOPE_KEYS = {
'domain_id', 'domain_name', 'domain_id',
'project_id', 'project_name', 'domain_name',
'system_scope' 'project_id',
'project_name',
'system_scope',
} }
# Sentinel for nonexistence # Sentinel for nonexistence
@ -90,9 +93,15 @@ def _get_implied_microversion(version):
return version return version
def from_session(session, name=None, region_name=None, def from_session(
force_ipv4=False, session,
app_name=None, app_version=None, **kwargs): name=None,
region_name=None,
force_ipv4=False,
app_name=None,
app_version=None,
**kwargs
):
"""Construct a CloudRegion from an existing `keystoneauth1.session.Session` """Construct a CloudRegion from an existing `keystoneauth1.session.Session`
When a Session already exists, we don't actually even need to go through When a Session already exists, we don't actually even need to go through
@ -118,9 +127,14 @@ def from_session(session, name=None, region_name=None,
config_dict = config_defaults.get_defaults() config_dict = config_defaults.get_defaults()
config_dict.update(**kwargs) config_dict.update(**kwargs)
return CloudRegion( return CloudRegion(
name=name, session=session, config=config_dict, name=name,
region_name=region_name, force_ipv4=force_ipv4, session=session,
app_name=app_name, app_version=app_version) config=config_dict,
region_name=region_name,
force_ipv4=force_ipv4,
app_name=app_name,
app_version=app_version,
)
def from_conf(conf, session=None, service_types=None, **kwargs): def from_conf(conf, session=None, service_types=None, **kwargs):
@ -160,8 +174,10 @@ def from_conf(conf, session=None, service_types=None, **kwargs):
for st in stm.all_types_by_service_type: for st in stm.all_types_by_service_type:
if service_types is not None and st not in service_types: if service_types is not None and st not in service_types:
_disable_service( _disable_service(
config_dict, st, config_dict,
reason="Not in the list of requested service_types.") st,
reason="Not in the list of requested service_types.",
)
continue continue
project_name = stm.get_project_name(st) project_name = stm.get_project_name(st)
if project_name not in conf: if project_name not in conf:
@ -170,10 +186,13 @@ def from_conf(conf, session=None, service_types=None, **kwargs):
if project_name not in conf: if project_name not in conf:
_disable_service( _disable_service(
config_dict, st, config_dict,
st,
reason="No section for project '{project}' (service type " reason="No section for project '{project}' (service type "
"'{service_type}') was present in the config." "'{service_type}') was present in the config.".format(
.format(project=project_name, service_type=st)) project=project_name, service_type=st
),
)
continue continue
opt_dict = {} opt_dict = {}
# Populate opt_dict with (appropriately processed) Adapter conf opts # Populate opt_dict with (appropriately processed) Adapter conf opts
@ -189,20 +208,24 @@ def from_conf(conf, session=None, service_types=None, **kwargs):
# option of) blowing up right away for (2) rather than letting them # option of) blowing up right away for (2) rather than letting them
# get all the way to the point of trying the service and having # get all the way to the point of trying the service and having
# *that* blow up. # *that* blow up.
reason = ("Encountered an exception attempting to process config " reason = (
"for project '{project}' (service type " "Encountered an exception attempting to process config "
"'{service_type}'): {exception}".format( "for project '{project}' (service type "
project=project_name, service_type=st, exception=e)) "'{service_type}'): {exception}".format(
_logger.warning("Disabling service '{service_type}': " project=project_name, service_type=st, exception=e
"{reason}".format(service_type=st, reason=reason)) )
)
_logger.warning(
"Disabling service '{service_type}': "
"{reason}".format(service_type=st, reason=reason)
)
_disable_service(config_dict, st, reason=reason) _disable_service(config_dict, st, reason=reason)
continue continue
# Load them into config_dict under keys prefixed by ${service_type}_ # Load them into config_dict under keys prefixed by ${service_type}_
for raw_name, opt_val in opt_dict.items(): for raw_name, opt_val in opt_dict.items():
config_name = _make_key(raw_name, st) config_name = _make_key(raw_name, st)
config_dict[config_name] = opt_val config_dict[config_name] = opt_val
return CloudRegion( return CloudRegion(session=session, config=config_dict, **kwargs)
session=session, config=config_dict, **kwargs)
class CloudRegion: class CloudRegion:
@ -232,18 +255,34 @@ class CloudRegion:
'interface': 'public' 'interface': 'public'
""" """
def __init__(self, name=None, region_name=None, config=None,
force_ipv4=False, auth_plugin=None, def __init__(
openstack_config=None, session_constructor=None, self,
app_name=None, app_version=None, session=None, name=None,
discovery_cache=None, extra_config=None, region_name=None,
cache_expiration_time=0, cache_expirations=None, config=None,
cache_path=None, cache_class='dogpile.cache.null', force_ipv4=False,
cache_arguments=None, password_callback=None, auth_plugin=None,
statsd_host=None, statsd_port=None, statsd_prefix=None, openstack_config=None,
influxdb_config=None, session_constructor=None,
collector_registry=None, app_name=None,
cache_auth=False): app_version=None,
session=None,
discovery_cache=None,
extra_config=None,
cache_expiration_time=0,
cache_expirations=None,
cache_path=None,
cache_class='dogpile.cache.null',
cache_arguments=None,
password_callback=None,
statsd_host=None,
statsd_port=None,
statsd_prefix=None,
influxdb_config=None,
collector_registry=None,
cache_auth=False,
):
self._name = name self._name = name
self.config = _util.normalize_keys(config) self.config = _util.normalize_keys(config)
# NOTE(efried): For backward compatibility: a) continue to accept the # NOTE(efried): For backward compatibility: a) continue to accept the
@ -294,9 +333,7 @@ class CloudRegion:
return self.config.__iter__() return self.config.__iter__()
def __eq__(self, other): def __eq__(self, other):
return ( return self.name == other.name and self.config == other.config
self.name == other.name
and self.config == other.config)
def __ne__(self, other): def __ne__(self, other):
return not self == other return not self == other
@ -306,7 +343,8 @@ class CloudRegion:
if self._name is None: if self._name is None:
try: try:
self._name = urllib.parse.urlparse( self._name = urllib.parse.urlparse(
self.get_session().auth.auth_url).hostname self.get_session().auth.auth_url
).hostname
except Exception: except Exception:
self._name = self._app_name or '' self._name = self._app_name or ''
return self._name return self._name
@ -352,7 +390,9 @@ class CloudRegion:
"You are specifying a cacert for the cloud {full_name}" "You are specifying a cacert for the cloud {full_name}"
" but also to ignore the host verification. The host SSL" " but also to ignore the host verification. The host SSL"
" cert will not be verified.".format( " cert will not be verified.".format(
full_name=self.full_name)) full_name=self.full_name
)
)
cert = self.config.get('cert') cert = self.config.get('cert')
if cert: if cert:
@ -365,19 +405,23 @@ class CloudRegion:
"""Return a list of service types we know something about.""" """Return a list of service types we know something about."""
services = [] services = []
for key, val in self.config.items(): for key, val in self.config.items():
if (key.endswith('api_version') if (
or key.endswith('service_type') key.endswith('api_version')
or key.endswith('service_name')): or key.endswith('service_type')
or key.endswith('service_name')
):
services.append("_".join(key.split('_')[:-2])) services.append("_".join(key.split('_')[:-2]))
return list(set(services)) return list(set(services))
def get_enabled_services(self): def get_enabled_services(self):
services = set() services = set()
all_services = [k['service_type'] for k in all_services = [
self._service_type_manager.services] k['service_type'] for k in self._service_type_manager.services
all_services.extend(k[4:] for k in ]
self.config.keys() if k.startswith('has_')) all_services.extend(
k[4:] for k in self.config.keys() if k.startswith('has_')
)
for srv in all_services: for srv in all_services:
ep = self.get_endpoint_from_catalog(srv) ep = self.get_endpoint_from_catalog(srv)
@ -390,10 +434,13 @@ class CloudRegion:
return self.config.get('auth', {}) return self.config.get('auth', {})
def _get_config( def _get_config(
self, key, service_type, self,
default=None, key,
fallback_to_unprefixed=False, service_type,
converter=None): default=None,
fallback_to_unprefixed=False,
converter=None,
):
'''Get a config value for a service_type. '''Get a config value for a service_type.
Finds the config value for a key, looking first for it prefixed by Finds the config value for a key, looking first for it prefixed by
@ -442,11 +489,13 @@ class CloudRegion:
# If a region_name for the specific service_type is configured, use it; # If a region_name for the specific service_type is configured, use it;
# else use the one configured for the CloudRegion as a whole. # else use the one configured for the CloudRegion as a whole.
return self._get_config( return self._get_config(
'region_name', service_type, fallback_to_unprefixed=True) 'region_name', service_type, fallback_to_unprefixed=True
)
def get_interface(self, service_type=None): def get_interface(self, service_type=None):
return self._get_config( return self._get_config(
'interface', service_type, fallback_to_unprefixed=True) 'interface', service_type, fallback_to_unprefixed=True
)
def get_api_version(self, service_type): def get_api_version(self, service_type):
version = self._get_config('api_version', service_type) version = self._get_config('api_version', service_type)
@ -458,7 +507,8 @@ class CloudRegion:
warnings.warn( warnings.warn(
"You have a configured API_VERSION with 'latest' in" "You have a configured API_VERSION with 'latest' in"
" it. In the context of openstacksdk this doesn't make" " it. In the context of openstacksdk this doesn't make"
" any sense.") " any sense."
)
return None return None
return version return version
@ -475,9 +525,11 @@ class CloudRegion:
# type will get us things in the right order. # type will get us things in the right order.
if self._service_type_manager.is_known(service_type): if self._service_type_manager.is_known(service_type):
service_type = self._service_type_manager.get_service_type( service_type = self._service_type_manager.get_service_type(
service_type) service_type
)
return self._get_config( return self._get_config(
'service_type', service_type, default=service_type) 'service_type', service_type, default=service_type
)
def get_service_name(self, service_type): def get_service_name(self, service_type):
return self._get_config('service_name', service_type) return self._get_config('service_name', service_type)
@ -492,8 +544,11 @@ class CloudRegion:
# then the endpoint value is the endpoint_override for every # then the endpoint value is the endpoint_override for every
# service. # service.
value = auth.get('endpoint') value = auth.get('endpoint')
if (not value and service_type == 'identity' if (
and SCOPE_KEYS.isdisjoint(set(auth.keys()))): not value
and service_type == 'identity'
and SCOPE_KEYS.isdisjoint(set(auth.keys()))
):
# There are a small number of unscoped identity operations. # There are a small number of unscoped identity operations.
# Specifically, looking up a list of projects/domains/system to # Specifically, looking up a list of projects/domains/system to
# scope to. # scope to.
@ -503,7 +558,8 @@ class CloudRegion:
# only v1 is in the catalog but the service actually does support # only v1 is in the catalog but the service actually does support
# v2. But the endpoint needs the project_id. # v2. But the endpoint needs the project_id.
service_type = self._service_type_manager.get_service_type( service_type = self._service_type_manager.get_service_type(
service_type) service_type
)
if ( if (
value value
and self.config.get('profile') == 'rackspace' and self.config.get('profile') == 'rackspace'
@ -513,7 +569,8 @@ class CloudRegion:
return value return value
def get_endpoint_from_catalog( def get_endpoint_from_catalog(
self, service_type, interface=None, region_name=None): self, service_type, interface=None, region_name=None
):
"""Return the endpoint for a given service as found in the catalog. """Return the endpoint for a given service as found in the catalog.
For values respecting endpoint overrides, see For values respecting endpoint overrides, see
@ -537,19 +594,26 @@ class CloudRegion:
return catalog.url_for( return catalog.url_for(
service_type=service_type, service_type=service_type,
interface=interface, interface=interface,
region_name=region_name) region_name=region_name,
)
except keystoneauth1.exceptions.catalog.EndpointNotFound: except keystoneauth1.exceptions.catalog.EndpointNotFound:
return None return None
def get_connect_retries(self, service_type): def get_connect_retries(self, service_type):
return self._get_config('connect_retries', service_type, return self._get_config(
fallback_to_unprefixed=True, 'connect_retries',
converter=int) service_type,
fallback_to_unprefixed=True,
converter=int,
)
def get_status_code_retries(self, service_type): def get_status_code_retries(self, service_type):
return self._get_config('status_code_retries', service_type, return self._get_config(
fallback_to_unprefixed=True, 'status_code_retries',
converter=int) service_type,
fallback_to_unprefixed=True,
converter=int,
)
@property @property
def prefer_ipv6(self): def prefer_ipv6(self):
@ -612,14 +676,16 @@ class CloudRegion:
desirable. desirable.
""" """
self._keystone_session.additional_user_agent.append( self._keystone_session.additional_user_agent.append(
('openstacksdk', openstack_version.__version__)) ('openstacksdk', openstack_version.__version__)
)
def get_session(self): def get_session(self):
"""Return a keystoneauth session based on the auth credentials.""" """Return a keystoneauth session based on the auth credentials."""
if self._keystone_session is None: if self._keystone_session is None:
if not self._auth: if not self._auth:
raise exceptions.ConfigException( raise exceptions.ConfigException(
"Problem with auth parameters") "Problem with auth parameters"
)
(verify, cert) = self.get_requests_verify_args() (verify, cert) = self.get_requests_verify_args()
# Turn off urllib3 warnings about insecure certs if we have # Turn off urllib3 warnings about insecure certs if we have
# explicitly configured requests to tell it we do not want # explicitly configured requests to tell it we do not want
@ -627,7 +693,8 @@ class CloudRegion:
if not verify: if not verify:
self.log.debug( self.log.debug(
"Turning off SSL warnings for {full_name}" "Turning off SSL warnings for {full_name}"
" since verify=False".format(full_name=self.full_name)) " since verify=False".format(full_name=self.full_name)
)
requestsexceptions.squelch_warnings(insecure_requests=not verify) requestsexceptions.squelch_warnings(insecure_requests=not verify)
self._keystone_session = self._session_constructor( self._keystone_session = self._session_constructor(
auth=self._auth, auth=self._auth,
@ -635,7 +702,8 @@ class CloudRegion:
cert=cert, cert=cert,
timeout=self.config.get('api_timeout'), timeout=self.config.get('api_timeout'),
collect_timing=self.config.get('timing'), collect_timing=self.config.get('timing'),
discovery_cache=self._discovery_cache) discovery_cache=self._discovery_cache,
)
self.insert_user_agent() self.insert_user_agent()
# Using old keystoneauth with new os-client-config fails if # Using old keystoneauth with new os-client-config fails if
# we pass in app_name and app_version. Those are not essential, # we pass in app_name and app_version. Those are not essential,
@ -683,15 +751,20 @@ class CloudRegion:
default_microversion = self.get_default_microversion(service_type) default_microversion = self.get_default_microversion(service_type)
implied_microversion = _get_implied_microversion(version) implied_microversion = _get_implied_microversion(version)
if (implied_microversion and default_microversion if (
and implied_microversion != default_microversion): implied_microversion
and default_microversion
and implied_microversion != default_microversion
):
raise exceptions.ConfigException( raise exceptions.ConfigException(
"default_microversion of {default_microversion} was given" "default_microversion of {default_microversion} was given"
" for {service_type}, but api_version looks like a" " for {service_type}, but api_version looks like a"
" microversion as well. Please set api_version to just the" " microversion as well. Please set api_version to just the"
" desired major version, or omit default_microversion".format( " desired major version, or omit default_microversion".format(
default_microversion=default_microversion, default_microversion=default_microversion,
service_type=service_type)) service_type=service_type,
)
)
if implied_microversion: if implied_microversion:
default_microversion = implied_microversion default_microversion = implied_microversion
# If we're inferring a microversion, don't pass the whole # If we're inferring a microversion, don't pass the whole
@ -715,7 +788,8 @@ class CloudRegion:
) )
region_versions = versions.get(region_name, {}) region_versions = versions.get(region_name, {})
interface_versions = region_versions.get( interface_versions = region_versions.get(
self.get_interface(service_type), {}) self.get_interface(service_type), {}
)
return interface_versions.get(service_type, []) return interface_versions.get(service_type, [])
def _get_endpoint_from_catalog(self, service_type, constructor): def _get_endpoint_from_catalog(self, service_type, constructor):
@ -729,8 +803,7 @@ class CloudRegion:
return adapter.get_endpoint() return adapter.get_endpoint()
def _get_hardcoded_endpoint(self, service_type, constructor): def _get_hardcoded_endpoint(self, service_type, constructor):
endpoint = self._get_endpoint_from_catalog( endpoint = self._get_endpoint_from_catalog(service_type, constructor)
service_type, constructor)
if not endpoint.rstrip().rsplit('/')[-1] == 'v2.0': if not endpoint.rstrip().rsplit('/')[-1] == 'v2.0':
if not endpoint.endswith('/'): if not endpoint.endswith('/'):
endpoint += '/' endpoint += '/'
@ -738,9 +811,8 @@ class CloudRegion:
return endpoint return endpoint
def get_session_client( def get_session_client(
self, service_type, version=None, self, service_type, version=None, constructor=proxy.Proxy, **kwargs
constructor=proxy.Proxy, ):
**kwargs):
"""Return a prepped keystoneauth Adapter for a given service. """Return a prepped keystoneauth Adapter for a given service.
This is useful for making direct requests calls against a This is useful for making direct requests calls against a
@ -757,23 +829,28 @@ class CloudRegion:
version_request = self._get_version_request(service_type, version) version_request = self._get_version_request(service_type, version)
kwargs.setdefault('region_name', self.get_region_name(service_type)) kwargs.setdefault('region_name', self.get_region_name(service_type))
kwargs.setdefault('connect_retries', kwargs.setdefault(
self.get_connect_retries(service_type)) 'connect_retries', self.get_connect_retries(service_type)
kwargs.setdefault('status_code_retries', )
self.get_status_code_retries(service_type)) kwargs.setdefault(
'status_code_retries', self.get_status_code_retries(service_type)
)
kwargs.setdefault('statsd_prefix', self.get_statsd_prefix()) kwargs.setdefault('statsd_prefix', self.get_statsd_prefix())
kwargs.setdefault('statsd_client', self.get_statsd_client()) kwargs.setdefault('statsd_client', self.get_statsd_client())
kwargs.setdefault('prometheus_counter', self.get_prometheus_counter()) kwargs.setdefault('prometheus_counter', self.get_prometheus_counter())
kwargs.setdefault( kwargs.setdefault(
'prometheus_histogram', self.get_prometheus_histogram()) 'prometheus_histogram', self.get_prometheus_histogram()
)
kwargs.setdefault('influxdb_config', self._influxdb_config) kwargs.setdefault('influxdb_config', self._influxdb_config)
kwargs.setdefault('influxdb_client', self.get_influxdb_client()) kwargs.setdefault('influxdb_client', self.get_influxdb_client())
endpoint_override = self.get_endpoint(service_type) endpoint_override = self.get_endpoint(service_type)
version = version_request.version version = version_request.version
min_api_version = ( min_api_version = (
kwargs.pop('min_version', None) or version_request.min_api_version) kwargs.pop('min_version', None) or version_request.min_api_version
)
max_api_version = ( max_api_version = (
kwargs.pop('max_version', None) or version_request.max_api_version) kwargs.pop('max_version', None) or version_request.max_api_version
)
# Older neutron has inaccessible discovery document. Nobody noticed # Older neutron has inaccessible discovery document. Nobody noticed
# because neutronclient hard-codes an append of v2.0. YAY! # because neutronclient hard-codes an append of v2.0. YAY!
@ -784,7 +861,8 @@ class CloudRegion:
max_api_version = None max_api_version = None
if endpoint_override is None: if endpoint_override is None:
endpoint_override = self._get_hardcoded_endpoint( endpoint_override = self._get_hardcoded_endpoint(
service_type, constructor) service_type, constructor
)
client = constructor( client = constructor(
session=self.get_session(), session=self.get_session(),
@ -798,14 +876,15 @@ class CloudRegion:
default_microversion=version_request.default_microversion, default_microversion=version_request.default_microversion,
rate_limit=self.get_rate_limit(service_type), rate_limit=self.get_rate_limit(service_type),
concurrency=self.get_concurrency(service_type), concurrency=self.get_concurrency(service_type),
**kwargs) **kwargs
)
if version_request.default_microversion: if version_request.default_microversion:
default_microversion = version_request.default_microversion default_microversion = version_request.default_microversion
info = client.get_endpoint_data() info = client.get_endpoint_data()
if not discover.version_between( if not discover.version_between(
info.min_microversion, info.min_microversion,
info.max_microversion, info.max_microversion,
default_microversion default_microversion,
): ):
if self.get_default_microversion(service_type): if self.get_default_microversion(service_type):
raise exceptions.ConfigException( raise exceptions.ConfigException(
@ -816,9 +895,13 @@ class CloudRegion:
service_type=service_type, service_type=service_type,
default_microversion=default_microversion, default_microversion=default_microversion,
min_microversion=discover.version_to_string( min_microversion=discover.version_to_string(
info.min_microversion), info.min_microversion
),
max_microversion=discover.version_to_string( max_microversion=discover.version_to_string(
info.max_microversion))) info.max_microversion
),
)
)
else: else:
raise exceptions.ConfigException( raise exceptions.ConfigException(
"A default microversion for service {service_type} of" "A default microversion for service {service_type} of"
@ -836,13 +919,18 @@ class CloudRegion:
api_version=self.get_api_version(service_type), api_version=self.get_api_version(service_type),
default_microversion=default_microversion, default_microversion=default_microversion,
min_microversion=discover.version_to_string( min_microversion=discover.version_to_string(
info.min_microversion), info.min_microversion
),
max_microversion=discover.version_to_string( max_microversion=discover.version_to_string(
info.max_microversion))) info.max_microversion
),
)
)
return client return client
def get_session_endpoint( def get_session_endpoint(
self, service_type, min_version=None, max_version=None): self, service_type, min_version=None, max_version=None
):
"""Return the endpoint from config or the catalog. """Return the endpoint from config or the catalog.
If a configuration lists an explicit endpoint for a service, If a configuration lists an explicit endpoint for a service,
@ -934,38 +1022,50 @@ class CloudRegion:
def get_external_networks(self): def get_external_networks(self):
"""Get list of network names for external networks.""" """Get list of network names for external networks."""
return [ return [
net['name'] for net in self.config.get('networks', []) net['name']
if net['routes_externally']] for net in self.config.get('networks', [])
if net['routes_externally']
]
def get_external_ipv4_networks(self): def get_external_ipv4_networks(self):
"""Get list of network names for external IPv4 networks.""" """Get list of network names for external IPv4 networks."""
return [ return [
net['name'] for net in self.config.get('networks', []) net['name']
if net['routes_ipv4_externally']] for net in self.config.get('networks', [])
if net['routes_ipv4_externally']
]
def get_external_ipv6_networks(self): def get_external_ipv6_networks(self):
"""Get list of network names for external IPv6 networks.""" """Get list of network names for external IPv6 networks."""
return [ return [
net['name'] for net in self.config.get('networks', []) net['name']
if net['routes_ipv6_externally']] for net in self.config.get('networks', [])
if net['routes_ipv6_externally']
]
def get_internal_networks(self): def get_internal_networks(self):
"""Get list of network names for internal networks.""" """Get list of network names for internal networks."""
return [ return [
net['name'] for net in self.config.get('networks', []) net['name']
if not net['routes_externally']] for net in self.config.get('networks', [])
if not net['routes_externally']
]
def get_internal_ipv4_networks(self): def get_internal_ipv4_networks(self):
"""Get list of network names for internal IPv4 networks.""" """Get list of network names for internal IPv4 networks."""
return [ return [
net['name'] for net in self.config.get('networks', []) net['name']
if not net['routes_ipv4_externally']] for net in self.config.get('networks', [])
if not net['routes_ipv4_externally']
]
def get_internal_ipv6_networks(self): def get_internal_ipv6_networks(self):
"""Get list of network names for internal IPv6 networks.""" """Get list of network names for internal IPv6 networks."""
return [ return [
net['name'] for net in self.config.get('networks', []) net['name']
if not net['routes_ipv6_externally']] for net in self.config.get('networks', [])
if not net['routes_ipv6_externally']
]
def get_default_network(self): def get_default_network(self):
"""Get network used for default interactions.""" """Get network used for default interactions."""
@ -999,8 +1099,8 @@ class CloudRegion:
if not key: if not key:
return defaults return defaults
return _util.merge_clouds( return _util.merge_clouds(
defaults, defaults, _util.normalize_keys(self._extra_config.get(key, {}))
_util.normalize_keys(self._extra_config.get(key, {}))) )
def get_client_config(self, name=None, defaults=None): def get_client_config(self, name=None, defaults=None):
"""Get config settings for a named client. """Get config settings for a named client.
@ -1020,25 +1120,29 @@ class CloudRegion:
client section and the defaults. client section and the defaults.
""" """
return self._get_extra_config( return self._get_extra_config(
name, self._get_extra_config('client', defaults)) name, self._get_extra_config('client', defaults)
)
def get_password_callback(self): def get_password_callback(self):
return self._password_callback return self._password_callback
def get_rate_limit(self, service_type=None): def get_rate_limit(self, service_type=None):
return self._get_service_config( return self._get_service_config(
'rate_limit', service_type=service_type) 'rate_limit', service_type=service_type
)
def get_concurrency(self, service_type=None): def get_concurrency(self, service_type=None):
return self._get_service_config( return self._get_service_config(
'concurrency', service_type=service_type) 'concurrency', service_type=service_type
)
def get_statsd_client(self): def get_statsd_client(self):
if not statsd: if not statsd:
if self._statsd_host: if self._statsd_host:
self.log.warning( self.log.warning(
'StatsD python library is not available. ' 'StatsD python library is not available. '
'Reporting disabled') 'Reporting disabled'
)
return None return None
statsd_args = {} statsd_args = {}
if self._statsd_host: if self._statsd_host:
@ -1075,7 +1179,10 @@ class CloudRegion:
'openstack_http_response_time', 'openstack_http_response_time',
'Time taken for an http response to an OpenStack service', 'Time taken for an http response to an OpenStack service',
labelnames=[ labelnames=[
'method', 'endpoint', 'service_type', 'status_code' 'method',
'endpoint',
'service_type',
'status_code',
], ],
registry=registry, registry=registry,
) )
@ -1092,7 +1199,10 @@ class CloudRegion:
'openstack_http_requests', 'openstack_http_requests',
'Number of HTTP requests made to an OpenStack service', 'Number of HTTP requests made to an OpenStack service',
labelnames=[ labelnames=[
'method', 'endpoint', 'service_type', 'status_code' 'method',
'endpoint',
'service_type',
'status_code',
], ],
registry=registry, registry=registry,
) )
@ -1103,7 +1213,8 @@ class CloudRegion:
service_type = service_type.lower().replace('-', '_') service_type = service_type.lower().replace('-', '_')
key = 'has_{service_type}'.format(service_type=service_type) key = 'has_{service_type}'.format(service_type=service_type)
return self.config.get( return self.config.get(
key, self._service_type_manager.is_official(service_type)) key, self._service_type_manager.is_official(service_type)
)
def disable_service(self, service_type, reason=None): def disable_service(self, service_type, reason=None):
_disable_service(self.config, service_type, reason=reason) _disable_service(self.config, service_type, reason=reason)
@ -1140,6 +1251,8 @@ class CloudRegion:
except Exception: except Exception:
self.log.warning('Cannot establish connection to InfluxDB') self.log.warning('Cannot establish connection to InfluxDB')
else: else:
self.log.warning('InfluxDB configuration is present, ' self.log.warning(
'but no client library is found.') 'InfluxDB configuration is present, '
'but no client library is found.'
)
return None return None

View File

@ -17,7 +17,8 @@ import os
import threading import threading
_json_path = os.path.join( _json_path = os.path.join(
os.path.dirname(os.path.realpath(__file__)), 'defaults.json') os.path.dirname(os.path.realpath(__file__)), 'defaults.json'
)
_defaults = None _defaults = None
_defaults_lock = threading.Lock() _defaults_lock = threading.Lock()

View File

@ -46,19 +46,23 @@ CACHE_PATH = APPDIRS.user_cache_dir
# see https://snapcraft.io/docs/environment-variables # see https://snapcraft.io/docs/environment-variables
SNAP_REAL_HOME = os.getenv('SNAP_REAL_HOME') SNAP_REAL_HOME = os.getenv('SNAP_REAL_HOME')
if SNAP_REAL_HOME: if SNAP_REAL_HOME:
UNIX_CONFIG_HOME = os.path.join(os.path.join(SNAP_REAL_HOME, '.config'), UNIX_CONFIG_HOME = os.path.join(
'openstack') os.path.join(SNAP_REAL_HOME, '.config'), 'openstack'
)
else: else:
UNIX_CONFIG_HOME = os.path.join( UNIX_CONFIG_HOME = os.path.join(
os.path.expanduser(os.path.join('~', '.config')), 'openstack') os.path.expanduser(os.path.join('~', '.config')), 'openstack'
)
UNIX_SITE_CONFIG_HOME = '/etc/openstack' UNIX_SITE_CONFIG_HOME = '/etc/openstack'
SITE_CONFIG_HOME = APPDIRS.site_config_dir SITE_CONFIG_HOME = APPDIRS.site_config_dir
CONFIG_SEARCH_PATH = [ CONFIG_SEARCH_PATH = [
os.getcwd(), os.getcwd(),
CONFIG_HOME, UNIX_CONFIG_HOME, CONFIG_HOME,
SITE_CONFIG_HOME, UNIX_SITE_CONFIG_HOME UNIX_CONFIG_HOME,
SITE_CONFIG_HOME,
UNIX_SITE_CONFIG_HOME,
] ]
YAML_SUFFIXES = ('.yaml', '.yml') YAML_SUFFIXES = ('.yaml', '.yml')
JSON_SUFFIXES = ('.json',) JSON_SUFFIXES = ('.json',)
@ -134,8 +138,8 @@ def _fix_argv(argv):
"The following options were given: '{options}' which contain" "The following options were given: '{options}' which contain"
" duplicates except that one has _ and one has -. There is" " duplicates except that one has _ and one has -. There is"
" no sane way for us to know what you're doing. Remove the" " no sane way for us to know what you're doing. Remove the"
" duplicate option and try again".format( " duplicate option and try again".format(options=','.join(overlap))
options=','.join(overlap))) )
class OpenStackConfig: class OpenStackConfig:
@ -146,14 +150,25 @@ class OpenStackConfig:
_cloud_region_class = cloud_region.CloudRegion _cloud_region_class = cloud_region.CloudRegion
_defaults_module = defaults _defaults_module = defaults
def __init__(self, config_files=None, vendor_files=None, def __init__(
override_defaults=None, force_ipv4=None, self,
envvar_prefix=None, secure_files=None, config_files=None,
pw_func=None, session_constructor=None, vendor_files=None,
app_name=None, app_version=None, override_defaults=None,
load_yaml_config=True, load_envvars=True, force_ipv4=None,
statsd_host=None, statsd_port=None, envvar_prefix=None,
statsd_prefix=None, influxdb_config=None): secure_files=None,
pw_func=None,
session_constructor=None,
app_name=None,
app_version=None,
load_yaml_config=True,
load_envvars=True,
statsd_host=None,
statsd_port=None,
statsd_prefix=None,
influxdb_config=None,
):
self.log = _log.setup_logging('openstack.config') self.log = _log.setup_logging('openstack.config')
self._session_constructor = session_constructor self._session_constructor = session_constructor
self._app_name = app_name self._app_name = app_name
@ -196,7 +211,8 @@ class OpenStackConfig:
_, secure_config = self._load_secure_file() _, secure_config = self._load_secure_file()
if secure_config: if secure_config:
self.cloud_config = _util.merge_clouds( self.cloud_config = _util.merge_clouds(
self.cloud_config, secure_config) self.cloud_config, secure_config
)
if not self.cloud_config: if not self.cloud_config:
self.cloud_config = {'clouds': {}} self.cloud_config = {'clouds': {}}
@ -217,14 +233,20 @@ class OpenStackConfig:
# Get the backwards compat value # Get the backwards compat value
prefer_ipv6 = get_boolean( prefer_ipv6 = get_boolean(
self._get_envvar( self._get_envvar(
'OS_PREFER_IPV6', client_config.get( 'OS_PREFER_IPV6',
'prefer_ipv6', client_config.get( client_config.get(
'prefer-ipv6', True)))) 'prefer_ipv6', client_config.get('prefer-ipv6', True)
),
)
)
force_ipv4 = get_boolean( force_ipv4 = get_boolean(
self._get_envvar( self._get_envvar(
'OS_FORCE_IPV4', client_config.get( 'OS_FORCE_IPV4',
'force_ipv4', client_config.get( client_config.get(
'broken-ipv6', False)))) 'force_ipv4', client_config.get('broken-ipv6', False)
),
)
)
self.force_ipv4 = force_ipv4 self.force_ipv4 = force_ipv4
if not prefer_ipv6: if not prefer_ipv6:
@ -239,8 +261,10 @@ class OpenStackConfig:
'"{0}" defines a cloud named "{1}", but' '"{0}" defines a cloud named "{1}", but'
' OS_CLOUD_NAME is also set to "{1}". Please rename' ' OS_CLOUD_NAME is also set to "{1}". Please rename'
' either your environment based cloud, or one of your' ' either your environment based cloud, or one of your'
' file-based clouds.'.format(self.config_filename, ' file-based clouds.'.format(
self.envvar_key)) self.config_filename, self.envvar_key
)
)
self.default_cloud = self._get_envvar('OS_CLOUD') self.default_cloud = self._get_envvar('OS_CLOUD')
@ -259,15 +283,15 @@ class OpenStackConfig:
# clouds.yaml. # clouds.yaml.
# The next/iter thing is for python3 compat where dict.keys # The next/iter thing is for python3 compat where dict.keys
# returns an iterator but in python2 it's a list. # returns an iterator but in python2 it's a list.
self.default_cloud = next(iter( self.default_cloud = next(
self.cloud_config['clouds'].keys())) iter(self.cloud_config['clouds'].keys())
)
# Finally, fall through and make a cloud that starts with defaults # Finally, fall through and make a cloud that starts with defaults
# because we need somewhere to put arguments, and there are neither # because we need somewhere to put arguments, and there are neither
# config files or env vars # config files or env vars
if not self.cloud_config['clouds']: if not self.cloud_config['clouds']:
self.cloud_config = dict( self.cloud_config = dict(clouds=dict(defaults=dict(self.defaults)))
clouds=dict(defaults=dict(self.defaults)))
self.default_cloud = 'defaults' self.default_cloud = 'defaults'
self._cache_auth = False self._cache_auth = False
@ -281,13 +305,15 @@ class OpenStackConfig:
cache_settings = _util.normalize_keys(self.cloud_config['cache']) cache_settings = _util.normalize_keys(self.cloud_config['cache'])
self._cache_auth = get_boolean( self._cache_auth = get_boolean(
cache_settings.get('auth', self._cache_auth)) cache_settings.get('auth', self._cache_auth)
)
# expiration_time used to be 'max_age' but the dogpile setting # expiration_time used to be 'max_age' but the dogpile setting
# is expiration_time. Support max_age for backwards compat. # is expiration_time. Support max_age for backwards compat.
self._cache_expiration_time = cache_settings.get( self._cache_expiration_time = cache_settings.get(
'expiration_time', cache_settings.get( 'expiration_time',
'max_age', self._cache_expiration_time)) cache_settings.get('max_age', self._cache_expiration_time),
)
# If cache class is given, use that. If not, but if cache time # If cache class is given, use that. If not, but if cache time
# is given, default to memory. Otherwise, default to nothing. # is given, default to memory. Otherwise, default to nothing.
@ -295,14 +321,18 @@ class OpenStackConfig:
if self._cache_expiration_time: if self._cache_expiration_time:
self._cache_class = 'dogpile.cache.memory' self._cache_class = 'dogpile.cache.memory'
self._cache_class = self.cloud_config['cache'].get( self._cache_class = self.cloud_config['cache'].get(
'class', self._cache_class) 'class', self._cache_class
)
self._cache_path = os.path.expanduser( self._cache_path = os.path.expanduser(
cache_settings.get('path', self._cache_path)) cache_settings.get('path', self._cache_path)
)
self._cache_arguments = cache_settings.get( self._cache_arguments = cache_settings.get(
'arguments', self._cache_arguments) 'arguments', self._cache_arguments
)
self._cache_expirations = cache_settings.get( self._cache_expirations = cache_settings.get(
'expiration', self._cache_expirations) 'expiration', self._cache_expirations
)
if load_yaml_config: if load_yaml_config:
metrics_config = self.cloud_config.get('metrics', {}) metrics_config = self.cloud_config.get('metrics', {})
@ -326,12 +356,21 @@ class OpenStackConfig:
use_udp = use_udp.lower() in ('true', 'yes', '1') use_udp = use_udp.lower() in ('true', 'yes', '1')
elif not isinstance(use_udp, bool): elif not isinstance(use_udp, bool):
use_udp = False use_udp = False
self.log.warning('InfluxDB.use_udp value type is not ' self.log.warning(
'supported. Use one of ' 'InfluxDB.use_udp value type is not '
'[true|false|yes|no|1|0]') 'supported. Use one of '
'[true|false|yes|no|1|0]'
)
config['use_udp'] = use_udp config['use_udp'] = use_udp
for key in ['host', 'port', 'username', 'password', 'database', for key in [
'measurement', 'timeout']: 'host',
'port',
'username',
'password',
'database',
'measurement',
'timeout',
]:
if key in influxdb_config: if key in influxdb_config:
config[key] = influxdb_config[key] config[key] = influxdb_config[key]
self._influxdb_config = config self._influxdb_config = config
@ -357,20 +396,28 @@ class OpenStackConfig:
if not envvar_prefix: if not envvar_prefix:
# This makes the or below be OS_ or OS_ which is a no-op # This makes the or below be OS_ or OS_ which is a no-op
envvar_prefix = 'OS_' envvar_prefix = 'OS_'
environkeys = [k for k in os.environ.keys() environkeys = [
if (k.startswith('OS_') or k.startswith(envvar_prefix)) k
and not k.startswith('OS_TEST') # infra CI var for k in os.environ.keys()
and not k.startswith('OS_STD') # oslotest var if (k.startswith('OS_') or k.startswith(envvar_prefix))
and not k.startswith('OS_LOG') # oslotest var and not k.startswith('OS_TEST') # infra CI var
] and not k.startswith('OS_STD') # oslotest var
and not k.startswith('OS_LOG') # oslotest var
]
for k in environkeys: for k in environkeys:
newkey = k.split('_', 1)[-1].lower() newkey = k.split('_', 1)[-1].lower()
ret[newkey] = os.environ[k] ret[newkey] = os.environ[k]
# If the only environ keys are selectors or behavior modification, # If the only environ keys are selectors or behavior modification,
# don't return anything # don't return anything
selectors = set([ selectors = set(
'OS_CLOUD', 'OS_REGION_NAME', [
'OS_CLIENT_CONFIG_FILE', 'OS_CLIENT_SECURE_FILE', 'OS_CLOUD_NAME']) 'OS_CLOUD',
'OS_REGION_NAME',
'OS_CLIENT_CONFIG_FILE',
'OS_CLIENT_SECURE_FILE',
'OS_CLOUD_NAME',
]
)
if set(environkeys) - selectors: if set(environkeys) - selectors:
return ret return ret
return None return None
@ -391,8 +438,8 @@ class OpenStackConfig:
if not key: if not key:
return defaults return defaults
return _util.merge_clouds( return _util.merge_clouds(
defaults, defaults, _util.normalize_keys(self.cloud_config.get(key, {}))
_util.normalize_keys(self.cloud_config.get(key, {}))) )
def _load_config_file(self): def _load_config_file(self):
return self._load_yaml_json_file(self._config_files) return self._load_yaml_json_file(self._config_files)
@ -427,10 +474,12 @@ class OpenStackConfig:
for region in regions: for region in regions:
if isinstance(region, dict): if isinstance(region, dict):
# i.e. must have name key, and only name,values keys # i.e. must have name key, and only name,values keys
if 'name' not in region or \ if 'name' not in region or not {'name', 'values'} >= set(
not {'name', 'values'} >= set(region): region
):
raise exceptions.ConfigException( raise exceptions.ConfigException(
'Invalid region entry at: %s' % region) 'Invalid region entry at: %s' % region
)
if 'values' not in region: if 'values' not in region:
region['values'] = {} region['values'] = {}
ret.append(copy.deepcopy(region)) ret.append(copy.deepcopy(region))
@ -460,7 +509,8 @@ class OpenStackConfig:
warnings.warn( warnings.warn(
"Comma separated lists in region_name are deprecated." "Comma separated lists in region_name are deprecated."
" Please use a yaml list in the regions" " Please use a yaml list in the regions"
" parameter in {0} instead.".format(self.config_filename)) " parameter in {0} instead.".format(self.config_filename)
)
return self._expand_regions(regions) return self._expand_regions(regions)
else: else:
# crappit. we don't have a region defined. # crappit. we don't have a region defined.
@ -495,7 +545,9 @@ class OpenStackConfig:
' region names are case sensitive.'.format( ' region names are case sensitive.'.format(
region_name=region_name, region_name=region_name,
region_list=','.join([r['name'] for r in regions]), region_list=','.join([r['name'] for r in regions]),
cloud=cloud)) cloud=cloud,
)
)
def get_cloud_names(self): def get_cloud_names(self):
return self.cloud_config['clouds'].keys() return self.cloud_config['clouds'].keys()
@ -506,8 +558,8 @@ class OpenStackConfig:
# Only validate cloud name if one was given # Only validate cloud name if one was given
if name and name not in self.cloud_config['clouds']: if name and name not in self.cloud_config['clouds']:
raise exceptions.ConfigException( raise exceptions.ConfigException(
"Cloud {name} was not found.".format( "Cloud {name} was not found.".format(name=name)
name=name)) )
our_cloud = self.cloud_config['clouds'].get(name, dict()) our_cloud = self.cloud_config['clouds'].get(name, dict())
if profile: if profile:
@ -536,11 +588,15 @@ class OpenStackConfig:
warnings.warn( warnings.warn(
"{0} uses the keyword 'cloud' to reference a known " "{0} uses the keyword 'cloud' to reference a known "
"vendor profile. This has been deprecated in favor of the " "vendor profile. This has been deprecated in favor of the "
"'profile' keyword.".format(self.config_filename)) "'profile' keyword.".format(self.config_filename)
)
vendor_filename, vendor_file = self._load_vendor_file() vendor_filename, vendor_file = self._load_vendor_file()
if (vendor_file and 'public-clouds' in vendor_file if (
and profile_name in vendor_file['public-clouds']): vendor_file
and 'public-clouds' in vendor_file
and profile_name in vendor_file['public-clouds']
):
_auth_update(cloud, vendor_file['public-clouds'][profile_name]) _auth_update(cloud, vendor_file['public-clouds'][profile_name])
else: else:
profile_data = vendors.get_profile(profile_name) profile_data = vendors.get_profile(profile_name)
@ -555,23 +611,31 @@ class OpenStackConfig:
if status == 'deprecated': if status == 'deprecated':
warnings.warn( warnings.warn(
"{profile_name} is deprecated: {message}".format( "{profile_name} is deprecated: {message}".format(
profile_name=profile_name, message=message)) profile_name=profile_name, message=message
)
)
elif status == 'shutdown': elif status == 'shutdown':
raise exceptions.ConfigException( raise exceptions.ConfigException(
"{profile_name} references a cloud that no longer" "{profile_name} references a cloud that no longer"
" exists: {message}".format( " exists: {message}".format(
profile_name=profile_name, message=message)) profile_name=profile_name, message=message
)
)
_auth_update(cloud, profile_data) _auth_update(cloud, profile_data)
else: else:
# Can't find the requested vendor config, go about business # Can't find the requested vendor config, go about business
warnings.warn("Couldn't find the vendor profile '{0}', for" warnings.warn(
" the cloud '{1}'".format(profile_name, "Couldn't find the vendor profile '{0}', for"
name)) " the cloud '{1}'".format(profile_name, name)
)
def _project_scoped(self, cloud): def _project_scoped(self, cloud):
return ('project_id' in cloud or 'project_name' in cloud return (
or 'project_id' in cloud['auth'] 'project_id' in cloud
or 'project_name' in cloud['auth']) or 'project_name' in cloud
or 'project_id' in cloud['auth']
or 'project_name' in cloud['auth']
)
def _validate_networks(self, networks, key): def _validate_networks(self, networks, key):
value = None value = None
@ -580,9 +644,9 @@ class OpenStackConfig:
raise exceptions.ConfigException( raise exceptions.ConfigException(
"Duplicate network entries for {key}: {net1} and {net2}." "Duplicate network entries for {key}: {net1} and {net2}."
" Only one network can be flagged with {key}".format( " Only one network can be flagged with {key}".format(
key=key, key=key, net1=value['name'], net2=net['name']
net1=value['name'], )
net2=net['name'])) )
if not value and net[key]: if not value and net[key]:
value = net value = net
@ -595,7 +659,8 @@ class OpenStackConfig:
name = net.get('name') name = net.get('name')
if not name: if not name:
raise exceptions.ConfigException( raise exceptions.ConfigException(
'Entry in network list is missing required field "name".') 'Entry in network list is missing required field "name".'
)
network = dict( network = dict(
name=name, name=name,
routes_externally=get_boolean(net.get('routes_externally')), routes_externally=get_boolean(net.get('routes_externally')),
@ -605,12 +670,12 @@ class OpenStackConfig:
) )
# routes_ipv4_externally defaults to the value of routes_externally # routes_ipv4_externally defaults to the value of routes_externally
network['routes_ipv4_externally'] = get_boolean( network['routes_ipv4_externally'] = get_boolean(
net.get( net.get('routes_ipv4_externally', network['routes_externally'])
'routes_ipv4_externally', network['routes_externally'])) )
# routes_ipv6_externally defaults to the value of routes_externally # routes_ipv6_externally defaults to the value of routes_externally
network['routes_ipv6_externally'] = get_boolean( network['routes_ipv6_externally'] = get_boolean(
net.get( net.get('routes_ipv6_externally', network['routes_externally'])
'routes_ipv6_externally', network['routes_externally'])) )
networks.append(network) networks.append(network)
for key in ('external_network', 'internal_network'): for key in ('external_network', 'internal_network'):
@ -619,18 +684,24 @@ class OpenStackConfig:
raise exceptions.ConfigException( raise exceptions.ConfigException(
"Both {key} and networks were specified in the config." "Both {key} and networks were specified in the config."
" Please remove {key} from the config and use the network" " Please remove {key} from the config and use the network"
" list to configure network behavior.".format(key=key)) " list to configure network behavior.".format(key=key)
)
if key in cloud: if key in cloud:
warnings.warn( warnings.warn(
"{key} is deprecated. Please replace with an entry in" "{key} is deprecated. Please replace with an entry in"
" a dict inside of the networks list with name: {name}" " a dict inside of the networks list with name: {name}"
" and routes_externally: {external}".format( " and routes_externally: {external}".format(
key=key, name=cloud[key], external=external)) key=key, name=cloud[key], external=external
networks.append(dict( )
name=cloud[key], )
routes_externally=external, networks.append(
nat_destination=not external, dict(
default_interface=external)) name=cloud[key],
routes_externally=external,
nat_destination=not external,
default_interface=external,
)
)
# Validate that we don't have duplicates # Validate that we don't have duplicates
self._validate_networks(networks, 'nat_destination') self._validate_networks(networks, 'nat_destination')
@ -668,7 +739,9 @@ class OpenStackConfig:
'user_domain_name': ('user_domain_name', 'user-domain-name'), 'user_domain_name': ('user_domain_name', 'user-domain-name'),
'project_domain_id': ('project_domain_id', 'project-domain-id'), 'project_domain_id': ('project_domain_id', 'project-domain-id'),
'project_domain_name': ( 'project_domain_name': (
'project_domain_name', 'project-domain-name'), 'project_domain_name',
'project-domain-name',
),
'token': ('auth-token', 'auth_token', 'token'), 'token': ('auth-token', 'auth_token', 'token'),
} }
if cloud.get('auth_type', None) == 'v2password': if cloud.get('auth_type', None) == 'v2password':
@ -676,14 +749,30 @@ class OpenStackConfig:
# clouds. That's fine - we need to map settings in the opposite # clouds. That's fine - we need to map settings in the opposite
# direction # direction
mappings['tenant_id'] = ( mappings['tenant_id'] = (
'project_id', 'project-id', 'tenant_id', 'tenant-id') 'project_id',
'project-id',
'tenant_id',
'tenant-id',
)
mappings['tenant_name'] = ( mappings['tenant_name'] = (
'project_name', 'project-name', 'tenant_name', 'tenant-name') 'project_name',
'project-name',
'tenant_name',
'tenant-name',
)
else: else:
mappings['project_id'] = ( mappings['project_id'] = (
'tenant_id', 'tenant-id', 'project_id', 'project-id') 'tenant_id',
'tenant-id',
'project_id',
'project-id',
)
mappings['project_name'] = ( mappings['project_name'] = (
'tenant_name', 'tenant-name', 'project_name', 'project-name') 'tenant_name',
'tenant-name',
'project_name',
'project-name',
)
for target_key, possible_values in mappings.items(): for target_key, possible_values in mappings.items():
target = None target = None
for key in possible_values: for key in possible_values:
@ -747,7 +836,8 @@ class OpenStackConfig:
'--os-cloud', '--os-cloud',
metavar='<name>', metavar='<name>',
default=self._get_envvar('OS_CLOUD', None), default=self._get_envvar('OS_CLOUD', None),
help='Named cloud to connect to') help='Named cloud to connect to',
)
# we need to peek to see if timeout was actually passed, since # we need to peek to see if timeout was actually passed, since
# the keystoneauth declaration of it has a default, which means # the keystoneauth declaration of it has a default, which means
@ -782,7 +872,8 @@ class OpenStackConfig:
try: try:
loading.register_auth_argparse_arguments( loading.register_auth_argparse_arguments(
parser, argv, default=default_auth_type) parser, argv, default=default_auth_type
)
except Exception: except Exception:
# Hidiing the keystoneauth exception because we're not actually # Hidiing the keystoneauth exception because we're not actually
# loading the auth plugin at this point, so the error message # loading the auth plugin at this point, so the error message
@ -793,7 +884,9 @@ class OpenStackConfig:
"An invalid auth-type was specified: {auth_type}." "An invalid auth-type was specified: {auth_type}."
" Valid choices are: {plugin_names}.".format( " Valid choices are: {plugin_names}.".format(
auth_type=options.os_auth_type, auth_type=options.os_auth_type,
plugin_names=",".join(plugin_names))) plugin_names=",".join(plugin_names),
)
)
if service_keys: if service_keys:
primary_service = service_keys[0] primary_service = service_keys[0]
@ -801,15 +894,19 @@ class OpenStackConfig:
primary_service = None primary_service = None
loading.register_session_argparse_arguments(parser) loading.register_session_argparse_arguments(parser)
adapter.register_adapter_argparse_arguments( adapter.register_adapter_argparse_arguments(
parser, service_type=primary_service) parser, service_type=primary_service
)
for service_key in service_keys: for service_key in service_keys:
# legacy clients have un-prefixed api-version options # legacy clients have un-prefixed api-version options
parser.add_argument( parser.add_argument(
'--{service_key}-api-version'.format( '--{service_key}-api-version'.format(
service_key=service_key.replace('_', '-')), service_key=service_key.replace('_', '-')
help=argparse_mod.SUPPRESS) ),
help=argparse_mod.SUPPRESS,
)
adapter.register_service_adapter_argparse_arguments( adapter.register_service_adapter_argparse_arguments(
parser, service_type=service_key) parser, service_type=service_key
)
# Backwards compat options for legacy clients # Backwards compat options for legacy clients
parser.add_argument('--http-timeout', help=argparse_mod.SUPPRESS) parser.add_argument('--http-timeout', help=argparse_mod.SUPPRESS)
@ -837,7 +934,8 @@ class OpenStackConfig:
service_timeout = None service_timeout = None
for key in cloud.keys(): for key in cloud.keys():
if key.endswith('timeout') and not ( if key.endswith('timeout') and not (
key == 'timeout' or key == 'api_timeout'): key == 'timeout' or key == 'api_timeout'
):
service_timeout = cloud[key] service_timeout = cloud[key]
else: else:
new_cloud[key] = cloud[key] new_cloud[key] = cloud[key]
@ -857,9 +955,11 @@ class OpenStackConfig:
for cloud in self.get_cloud_names(): for cloud in self.get_cloud_names():
for region in self._get_regions(cloud): for region in self._get_regions(cloud):
if region: if region:
clouds.append(self.get_one( clouds.append(
cloud, region_name=region['name'])) self.get_one(cloud, region_name=region['name'])
)
return clouds return clouds
# TODO(mordred) Backwards compat for OSC transition # TODO(mordred) Backwards compat for OSC transition
get_all_clouds = get_all get_all_clouds = get_all
@ -904,8 +1004,9 @@ class OpenStackConfig:
if opt_name in config: if opt_name in config:
return config[opt_name] return config[opt_name]
else: else:
deprecated = getattr(opt, 'deprecated', getattr( deprecated = getattr(
opt, 'deprecated_opts', [])) opt, 'deprecated', getattr(opt, 'deprecated_opts', [])
)
for d_opt in deprecated: for d_opt in deprecated:
d_opt_name = d_opt.name.replace('-', '_') d_opt_name = d_opt.name.replace('-', '_')
if d_opt_name in config: if d_opt_name in config:
@ -1027,9 +1128,9 @@ class OpenStackConfig:
def option_prompt(self, config, p_opt): def option_prompt(self, config, p_opt):
"""Prompt user for option that requires a value""" """Prompt user for option that requires a value"""
if ( if (
getattr(p_opt, 'prompt', None) is not None getattr(p_opt, 'prompt', None) is not None
and p_opt.dest not in config['auth'] and p_opt.dest not in config['auth']
and self._pw_callback is not None and self._pw_callback is not None
): ):
config['auth'][p_opt.dest] = self._pw_callback(p_opt.prompt) config['auth'][p_opt.dest] = self._pw_callback(p_opt.prompt)
return config return config
@ -1046,8 +1147,7 @@ class OpenStackConfig:
# Prefer the plugin configuration dest value if the value's key # Prefer the plugin configuration dest value if the value's key
# is marked as depreciated. # is marked as depreciated.
if p_opt.dest is None: if p_opt.dest is None:
config['auth'][p_opt.name.replace('-', '_')] = ( config['auth'][p_opt.name.replace('-', '_')] = winning_value
winning_value)
else: else:
config['auth'][p_opt.dest] = winning_value config['auth'][p_opt.dest] = winning_value
return config return config
@ -1056,9 +1156,11 @@ class OpenStackConfig:
"""Perform the set of magic argument fixups""" """Perform the set of magic argument fixups"""
# Infer token plugin if a token was given # Infer token plugin if a token was given
if (('auth' in config and 'token' in config['auth']) if (
or ('auth_token' in config and config['auth_token']) ('auth' in config and 'token' in config['auth'])
or ('token' in config and config['token'])): or ('auth_token' in config and config['auth_token'])
or ('token' in config and config['token'])
):
config.setdefault('token', config.pop('auth_token', None)) config.setdefault('token', config.pop('auth_token', None))
# Infer passcode if it was given separately # Infer passcode if it was given separately
@ -1094,12 +1196,12 @@ class OpenStackConfig:
# more generalized # more generalized
if 'auth' in config and 'auth_url' in config['auth']: if 'auth' in config and 'auth_url' in config['auth']:
config['auth']['auth_url'] = config['auth']['auth_url'].format( config['auth']['auth_url'] = config['auth']['auth_url'].format(
**config) **config
)
return config return config
def get_one( def get_one(self, cloud=None, validate=True, argparse=None, **kwargs):
self, cloud=None, validate=True, argparse=None, **kwargs):
"""Retrieve a single CloudRegion and merge additional options """Retrieve a single CloudRegion and merge additional options
:param string cloud: :param string cloud:
@ -1217,15 +1319,12 @@ class OpenStackConfig:
statsd_prefix=statsd_prefix, statsd_prefix=statsd_prefix,
influxdb_config=influxdb_config, influxdb_config=influxdb_config,
) )
# TODO(mordred) Backwards compat for OSC transition # TODO(mordred) Backwards compat for OSC transition
get_one_cloud = get_one get_one_cloud = get_one
def get_one_cloud_osc( def get_one_cloud_osc(
self, self, cloud=None, validate=True, argparse=None, **kwargs
cloud=None,
validate=True,
argparse=None,
**kwargs
): ):
"""Retrieve a single CloudRegion and merge additional options """Retrieve a single CloudRegion and merge additional options
@ -1359,10 +1458,10 @@ if __name__ == '__main__':
if len(sys.argv) == 1: if len(sys.argv) == 1:
print_cloud = True print_cloud = True
elif len(sys.argv) == 3 and ( elif len(sys.argv) == 3 and (
sys.argv[1] == cloud.name and sys.argv[2] == cloud.region): sys.argv[1] == cloud.name and sys.argv[2] == cloud.region
):
print_cloud = True print_cloud = True
elif len(sys.argv) == 2 and ( elif len(sys.argv) == 2 and (sys.argv[1] == cloud.name):
sys.argv[1] == cloud.name):
print_cloud = True print_cloud = True
if print_cloud: if print_cloud:

View File

@ -61,7 +61,9 @@ def get_profile(profile_name):
" {status_code} {reason}".format( " {status_code} {reason}".format(
profile_name=profile_name, profile_name=profile_name,
status_code=response.status_code, status_code=response.status_code,
reason=response.reason)) reason=response.reason,
)
)
vendor_defaults[profile_name] = None vendor_defaults[profile_name] = None
return return
vendor_data = response.json() vendor_data = response.json()
@ -69,8 +71,8 @@ def get_profile(profile_name):
# Merge named and url cloud config, but make named config override the # Merge named and url cloud config, but make named config override the
# config from the cloud so that we can supply local overrides if needed. # config from the cloud so that we can supply local overrides if needed.
profile = _util.merge_clouds( profile = _util.merge_clouds(
vendor_data['profile'], vendor_data['profile'], vendor_defaults.get(name, {})
vendor_defaults.get(name, {})) )
# If there is (or was) a profile listed in a named config profile, it # If there is (or was) a profile listed in a named config profile, it
# might still be here. We just merged in content from a URL though, so # might still be here. We just merged in content from a URL though, so
# pop the key to prevent doing it again in the future. # pop the key to prevent doing it again in the future.

View File

@ -220,7 +220,8 @@ __all__ = [
if requestsexceptions.SubjectAltNameWarning: if requestsexceptions.SubjectAltNameWarning:
warnings.filterwarnings( warnings.filterwarnings(
'ignore', category=requestsexceptions.SubjectAltNameWarning) 'ignore', category=requestsexceptions.SubjectAltNameWarning
)
_logger = _log.setup_logging('openstack') _logger = _log.setup_logging('openstack')
@ -249,7 +250,8 @@ def from_config(cloud=None, config=None, options=None, **kwargs):
config = kwargs.pop('cloud_config', config) config = kwargs.pop('cloud_config', config)
if config is None: if config is None:
config = _config.OpenStackConfig().get_one( config = _config.OpenStackConfig().get_one(
cloud=cloud, argparse=options, **kwargs) cloud=cloud, argparse=options, **kwargs
)
return Connection(config=config) return Connection(config=config)
@ -274,20 +276,25 @@ class Connection(
_security_group.SecurityGroupCloudMixin, _security_group.SecurityGroupCloudMixin,
_shared_file_system.SharedFileSystemCloudMixin, _shared_file_system.SharedFileSystemCloudMixin,
): ):
def __init__(
def __init__(self, cloud=None, config=None, session=None, self,
app_name=None, app_version=None, cloud=None,
extra_services=None, config=None,
strict=False, session=None,
use_direct_get=False, app_name=None,
task_manager=None, app_version=None,
rate_limit=None, extra_services=None,
oslo_conf=None, strict=False,
service_types=None, use_direct_get=False,
global_request_id=None, task_manager=None,
strict_proxies=False, rate_limit=None,
pool_executor=None, oslo_conf=None,
**kwargs): service_types=None,
global_request_id=None,
strict_proxies=False,
pool_executor=None,
**kwargs
):
"""Create a connection to a cloud. """Create a connection to a cloud.
A connection needs information about how to connect, how to A connection needs information about how to connect, how to
@ -373,24 +380,32 @@ class Connection(
if not self.config: if not self.config:
if oslo_conf: if oslo_conf:
self.config = cloud_region.from_conf( self.config = cloud_region.from_conf(
oslo_conf, session=session, app_name=app_name, oslo_conf,
app_version=app_version, service_types=service_types) session=session,
app_name=app_name,
app_version=app_version,
service_types=service_types,
)
elif session: elif session:
self.config = cloud_region.from_session( self.config = cloud_region.from_session(
session=session, session=session,
app_name=app_name, app_version=app_version, app_name=app_name,
app_version=app_version,
load_yaml_config=False, load_yaml_config=False,
load_envvars=False, load_envvars=False,
rate_limit=rate_limit, rate_limit=rate_limit,
**kwargs) **kwargs
)
else: else:
self.config = _config.get_cloud_region( self.config = _config.get_cloud_region(
cloud=cloud, cloud=cloud,
app_name=app_name, app_version=app_version, app_name=app_name,
app_version=app_version,
load_yaml_config=cloud is not None, load_yaml_config=cloud is not None,
load_envvars=cloud is not None, load_envvars=cloud is not None,
rate_limit=rate_limit, rate_limit=rate_limit,
**kwargs) **kwargs
)
self._session = None self._session = None
self._proxies = {} self._proxies = {}
@ -440,19 +455,25 @@ class Connection(
hook = ep.load() hook = ep.load()
hook(self) hook(self)
except ValueError: except ValueError:
self.log.warning('Hook should be in the entrypoint ' self.log.warning(
'module:attribute format') 'Hook should be in the entrypoint '
'module:attribute format'
)
except (ImportError, TypeError, AttributeError) as e: except (ImportError, TypeError, AttributeError) as e:
self.log.warning('Configured hook %s cannot be executed: %s', self.log.warning(
vendor_hook, e) 'Configured hook %s cannot be executed: %s', vendor_hook, e
)
# Add additional metrics into the configuration according to the # Add additional metrics into the configuration according to the
# selected connection. We don't want to deal with overall config in the # selected connection. We don't want to deal with overall config in the
# proxy, just pass required part. # proxy, just pass required part.
if (self.config._influxdb_config if (
and 'additional_metric_tags' in self.config.config): self.config._influxdb_config
self.config._influxdb_config['additional_metric_tags'] = \ and 'additional_metric_tags' in self.config.config
self.config.config['additional_metric_tags'] ):
self.config._influxdb_config[
'additional_metric_tags'
] = self.config.config['additional_metric_tags']
def __del__(self): def __del__(self):
# try to force release of resources and save authorization # try to force release of resources and save authorization
@ -500,7 +521,7 @@ class Connection(
setattr( setattr(
self.__class__, self.__class__,
attr_name.replace('-', '_'), attr_name.replace('-', '_'),
property(fget=getter) property(fget=getter),
) )
self.config.enable_service(service.service_type) self.config.enable_service(service.service_type)
@ -527,7 +548,8 @@ class Connection(
def _pool_executor(self): def _pool_executor(self):
if not self.__pool_executor: if not self.__pool_executor:
self.__pool_executor = concurrent.futures.ThreadPoolExecutor( self.__pool_executor = concurrent.futures.ThreadPoolExecutor(
max_workers=5) max_workers=5
)
return self.__pool_executor return self.__pool_executor
def close(self): def close(self):

View File

@ -24,6 +24,7 @@ from requests import exceptions as _rex
class SDKException(Exception): class SDKException(Exception):
"""The base exception class for all exceptions this library raises.""" """The base exception class for all exceptions this library raises."""
def __init__(self, message=None, extra_data=None): def __init__(self, message=None, extra_data=None):
self.message = self.__class__.__name__ if message is None else message self.message = self.__class__.__name__ if message is None else message
self.extra_data = extra_data self.extra_data = extra_data
@ -35,6 +36,7 @@ OpenStackCloudException = SDKException
class EndpointNotFound(SDKException): class EndpointNotFound(SDKException):
"""A mismatch occurred between what the client and server expect.""" """A mismatch occurred between what the client and server expect."""
def __init__(self, message=None): def __init__(self, message=None):
super(EndpointNotFound, self).__init__(message) super(EndpointNotFound, self).__init__(message)
@ -55,20 +57,25 @@ class InvalidRequest(SDKException):
class HttpException(SDKException, _rex.HTTPError): class HttpException(SDKException, _rex.HTTPError):
def __init__(
def __init__(self, message='Error', response=None, self,
http_status=None, message='Error',
details=None, request_id=None): response=None,
http_status=None,
details=None,
request_id=None,
):
# TODO(shade) Remove http_status parameter and the ability for response # TODO(shade) Remove http_status parameter and the ability for response
# to be None once we're not mocking Session everywhere. # to be None once we're not mocking Session everywhere.
if not message: if not message:
if response is not None: if response is not None:
message = "{name}: {code}".format( message = "{name}: {code}".format(
name=self.__class__.__name__, name=self.__class__.__name__, code=response.status_code
code=response.status_code) )
else: else:
message = "{name}: Unknown error".format( message = "{name}: Unknown error".format(
name=self.__class__.__name__) name=self.__class__.__name__
)
# Call directly rather than via super to control parameters # Call directly rather than via super to control parameters
SDKException.__init__(self, message=message) SDKException.__init__(self, message=message)
@ -96,7 +103,8 @@ class HttpException(SDKException, _rex.HTTPError):
return self.message return self.message
if self.url: if self.url:
remote_error = "{source} Error for url: {url}".format( remote_error = "{source} Error for url: {url}".format(
source=self.source, url=self.url) source=self.source, url=self.url
)
if self.details: if self.details:
remote_error += ', ' remote_error += ', '
if self.details: if self.details:
@ -104,31 +112,37 @@ class HttpException(SDKException, _rex.HTTPError):
return "{message}: {remote_error}".format( return "{message}: {remote_error}".format(
message=super(HttpException, self).__str__(), message=super(HttpException, self).__str__(),
remote_error=remote_error) remote_error=remote_error,
)
class BadRequestException(HttpException): class BadRequestException(HttpException):
"""HTTP 400 Bad Request.""" """HTTP 400 Bad Request."""
pass pass
class ForbiddenException(HttpException): class ForbiddenException(HttpException):
"""HTTP 403 Forbidden Request.""" """HTTP 403 Forbidden Request."""
pass pass
class ConflictException(HttpException): class ConflictException(HttpException):
"""HTTP 409 Conflict.""" """HTTP 409 Conflict."""
pass pass
class PreconditionFailedException(HttpException): class PreconditionFailedException(HttpException):
"""HTTP 412 Precondition Failed.""" """HTTP 412 Precondition Failed."""
pass pass
class MethodNotSupported(SDKException): class MethodNotSupported(SDKException):
"""The resource does not support this operation type.""" """The resource does not support this operation type."""
def __init__(self, resource, method): def __init__(self, resource, method):
# This needs to work with both classes and instances. # This needs to work with both classes and instances.
try: try:
@ -136,18 +150,23 @@ class MethodNotSupported(SDKException):
except AttributeError: except AttributeError:
name = resource.__class__.__name__ name = resource.__class__.__name__
message = ('The %s method is not supported for %s.%s' % message = 'The %s method is not supported for %s.%s' % (
(method, resource.__module__, name)) method,
resource.__module__,
name,
)
super(MethodNotSupported, self).__init__(message=message) super(MethodNotSupported, self).__init__(message=message)
class DuplicateResource(SDKException): class DuplicateResource(SDKException):
"""More than one resource exists with that name.""" """More than one resource exists with that name."""
pass pass
class ResourceNotFound(HttpException): class ResourceNotFound(HttpException):
"""No resource exists with that name or id.""" """No resource exists with that name or id."""
pass pass
@ -156,16 +175,19 @@ NotFoundException = ResourceNotFound
class ResourceTimeout(SDKException): class ResourceTimeout(SDKException):
"""Timeout waiting for resource.""" """Timeout waiting for resource."""
pass pass
class ResourceFailure(SDKException): class ResourceFailure(SDKException):
"""General resource failure.""" """General resource failure."""
pass pass
class InvalidResourceQuery(SDKException): class InvalidResourceQuery(SDKException):
"""Invalid query params for resource.""" """Invalid query params for resource."""
pass pass
@ -225,8 +247,9 @@ def raise_from_response(response, error_message=None):
details = response.text details = response.text
elif response.content and 'text/html' in content_type: elif response.content and 'text/html' in content_type:
# Split the lines, strip whitespace and inline HTML from the response. # Split the lines, strip whitespace and inline HTML from the response.
details = [re.sub(r'<.+?>', '', i.strip()) details = [
for i in response.text.splitlines()] re.sub(r'<.+?>', '', i.strip()) for i in response.text.splitlines()
]
details = list(set([msg for msg in details if msg])) details = list(set([msg for msg in details if msg]))
# Return joined string separated by colons. # Return joined string separated by colons.
details = ': '.join(details) details = ': '.join(details)
@ -238,8 +261,11 @@ def raise_from_response(response, error_message=None):
request_id = response.headers.get('x-openstack-request-id') request_id = response.headers.get('x-openstack-request-id')
raise cls( raise cls(
message=error_message, response=response, details=details, message=error_message,
http_status=http_status, request_id=request_id response=response,
details=details,
http_status=http_status,
request_id=request_id,
) )
@ -249,6 +275,7 @@ class UnsupportedServiceVersion(Warning):
class ArgumentDeprecationWarning(Warning): class ArgumentDeprecationWarning(Warning):
"""A deprecated argument has been provided.""" """A deprecated argument has been provided."""
pass pass

View File

@ -61,7 +61,8 @@ class ConnectionFixture(fixtures.Fixture):
templates = {} templates = {}
for k, v in self._endpoint_templates.items(): for k, v in self._endpoint_templates.items():
suffix = self._suffixes.get( suffix = self._suffixes.get(
alias, self._suffixes.get(service_type, '')) alias, self._suffixes.get(service_type, '')
)
# For a keystone v2 catalog, we want to list the # For a keystone v2 catalog, we want to list the
# versioned endpoint in the catalog, because that's # versioned endpoint in the catalog, because that's
# more likely how those were deployed. # more likely how those were deployed.
@ -88,10 +89,8 @@ class ConnectionFixture(fixtures.Fixture):
continue continue
service_name = service['project'] service_name = service['project']
ets = self._get_endpoint_templates(service_type) ets = self._get_endpoint_templates(service_type)
v3_svc = self.v3_token.add_service( v3_svc = self.v3_token.add_service(service_type, name=service_name)
service_type, name=service_name) v2_svc = self.v2_token.add_service(service_type, name=service_name)
v2_svc = self.v2_token.add_service(
service_type, name=service_name)
v3_svc.add_standard_endpoints(region='RegionOne', **ets) v3_svc.add_standard_endpoints(region='RegionOne', **ets)
if service_type == 'identity': if service_type == 'identity':
ets = self._get_endpoint_templates(service_type, v2=True) ets = self._get_endpoint_templates(service_type, v2=True)

View File

@ -12,7 +12,6 @@
class Formatter: class Formatter:
@classmethod @classmethod
def serialize(cls, value): def serialize(cls, value):
"""Return a string representing the formatted value""" """Return a string representing the formatted value"""
@ -25,7 +24,6 @@ class Formatter:
class BoolStr(Formatter): class BoolStr(Formatter):
@classmethod @classmethod
def deserialize(cls, value): def deserialize(cls, value):
"""Convert a boolean string to a boolean""" """Convert a boolean string to a boolean"""
@ -35,8 +33,9 @@ class BoolStr(Formatter):
elif "false" == expr: elif "false" == expr:
return False return False
else: else:
raise ValueError("Unable to deserialize boolean string: %s" raise ValueError(
% value) "Unable to deserialize boolean string: %s" % value
)
@classmethod @classmethod
def serialize(cls, value): def serialize(cls, value):
@ -47,5 +46,4 @@ class BoolStr(Formatter):
else: else:
return "false" return "false"
else: else:
raise ValueError("Unable to serialize boolean string: %s" raise ValueError("Unable to serialize boolean string: %s" % value)
% value)

View File

@ -437,8 +437,7 @@ class Proxy(adapter.Adapter, Generic[T]):
self, '_connection', getattr(self.session, '_sdk_connection', None) self, '_connection', getattr(self.session, '_sdk_connection', None)
) )
def _get_resource(self, resource_type: Type[T], value, def _get_resource(self, resource_type: Type[T], value, **attrs) -> T:
**attrs) -> T:
"""Get a resource object to work on """Get a resource object to work on
:param resource_type: The type of resource to operate on. This should :param resource_type: The type of resource to operate on. This should
@ -484,8 +483,9 @@ class Proxy(adapter.Adapter, Generic[T]):
value = resource.Resource._get_id(parent) value = resource.Resource._get_id(parent)
return value return value
def _find(self, resource_type: Type[T], name_or_id, ignore_missing=True, def _find(
**attrs) -> Optional[T]: self, resource_type: Type[T], name_or_id, ignore_missing=True, **attrs
) -> Optional[T]:
"""Find a resource """Find a resource
:param name_or_id: The name or ID of a resource to find. :param name_or_id: The name or ID of a resource to find.
@ -505,8 +505,9 @@ class Proxy(adapter.Adapter, Generic[T]):
) )
@_check_resource(strict=False) @_check_resource(strict=False)
def _delete(self, resource_type: Type[T], value, ignore_missing=True, def _delete(
**attrs): self, resource_type: Type[T], value, ignore_missing=True, **attrs
):
"""Delete a resource """Delete a resource
:param resource_type: The type of resource to delete. This should :param resource_type: The type of resource to delete. This should
@ -542,8 +543,9 @@ class Proxy(adapter.Adapter, Generic[T]):
return rv return rv
@_check_resource(strict=False) @_check_resource(strict=False)
def _update(self, resource_type: Type[T], value, base_path=None, def _update(
**attrs) -> T: self, resource_type: Type[T], value, base_path=None, **attrs
) -> T:
"""Update a resource """Update a resource
:param resource_type: The type of resource to update. :param resource_type: The type of resource to update.
@ -591,8 +593,9 @@ class Proxy(adapter.Adapter, Generic[T]):
res = resource_type.new(connection=conn, **attrs) res = resource_type.new(connection=conn, **attrs)
return res.create(self, base_path=base_path) return res.create(self, base_path=base_path)
def _bulk_create(self, resource_type: Type[T], data, base_path=None def _bulk_create(
) -> Generator[T, None, None]: self, resource_type: Type[T], data, base_path=None
) -> Generator[T, None, None]:
"""Create a resource from attributes """Create a resource from attributes
:param resource_type: The type of resource to create. :param resource_type: The type of resource to create.
@ -614,13 +617,13 @@ class Proxy(adapter.Adapter, Generic[T]):
@_check_resource(strict=False) @_check_resource(strict=False)
def _get( def _get(
self, self,
resource_type: Type[T], resource_type: Type[T],
value=None, value=None,
requires_id=True, requires_id=True,
base_path=None, base_path=None,
skip_cache=False, skip_cache=False,
**attrs **attrs
): ):
"""Fetch a resource """Fetch a resource
@ -657,12 +660,12 @@ class Proxy(adapter.Adapter, Generic[T]):
) )
def _list( def _list(
self, self,
resource_type: Type[T], resource_type: Type[T],
paginated=True, paginated=True,
base_path=None, base_path=None,
jmespath_filters=None, jmespath_filters=None,
**attrs **attrs
) -> Generator[T, None, None]: ) -> Generator[T, None, None]:
"""List a resource """List a resource
@ -690,8 +693,7 @@ class Proxy(adapter.Adapter, Generic[T]):
the ``resource_type``. the ``resource_type``.
""" """
data = resource_type.list( data = resource_type.list(
self, paginated=paginated, base_path=base_path, self, paginated=paginated, base_path=base_path, **attrs
**attrs
) )
if jmespath_filters and isinstance(jmespath_filters, str): if jmespath_filters and isinstance(jmespath_filters, str):
@ -699,8 +701,9 @@ class Proxy(adapter.Adapter, Generic[T]):
return data return data
def _head(self, resource_type: Type[T], value=None, base_path=None, def _head(
**attrs): self, resource_type: Type[T], value=None, base_path=None, **attrs
):
"""Retrieve a resource's header """Retrieve a resource's header
:param resource_type: The type of resource to retrieve. :param resource_type: The type of resource to retrieve.

View File

@ -207,15 +207,14 @@ class _BaseComponent:
def warn_if_deprecated_property(self, value): def warn_if_deprecated_property(self, value):
deprecated = object.__getattribute__(self, 'deprecated') deprecated = object.__getattribute__(self, 'deprecated')
deprecation_reason = object.__getattribute__( deprecation_reason = object.__getattribute__(
self, 'deprecation_reason', self,
'deprecation_reason',
) )
if value and deprecated: if value and deprecated:
warnings.warn( warnings.warn(
"The field %r has been deprecated. %s" % ( "The field %r has been deprecated. %s"
self.name, % (self.name, deprecation_reason or "Avoid usage."),
deprecation_reason or "Avoid usage."
),
os_warnings.RemovedFieldWarning, os_warnings.RemovedFieldWarning,
) )
return value return value
@ -1027,9 +1026,7 @@ class Resource(dict):
converted = [] converted = []
for raw in value: for raw in value:
if isinstance(raw, Resource): if isinstance(raw, Resource):
converted.append( converted.append(raw.to_dict(_to_munch=to_munch))
raw.to_dict(_to_munch=to_munch)
)
elif isinstance(raw, dict) and to_munch: elif isinstance(raw, dict) and to_munch:
converted.append(utils.Munch(raw)) converted.append(utils.Munch(raw))
else: else:
@ -1223,10 +1220,7 @@ class Resource(dict):
requires_id = self.requires_id requires_id = self.requires_id
# Conditionally construct arguments for _prepare_request_body # Conditionally construct arguments for _prepare_request_body
request_kwargs = { request_kwargs = {"patch": patch, "prepend_key": prepend_key}
"patch": patch,
"prepend_key": prepend_key
}
if resource_request_key is not None: if resource_request_key is not None:
request_kwargs['resource_request_key'] = resource_request_key request_kwargs['resource_request_key'] = resource_request_key
body = self._prepare_request_body(**request_kwargs) body = self._prepare_request_body(**request_kwargs)
@ -1443,7 +1437,7 @@ class Resource(dict):
resource_request_key=None, resource_request_key=None,
resource_response_key=None, resource_response_key=None,
microversion=None, microversion=None,
**params **params,
): ):
"""Create a remote resource based on this instance. """Create a remote resource based on this instance.
@ -1532,8 +1526,7 @@ class Resource(dict):
# fetch the body if it's required but not returned by create # fetch the body if it's required but not returned by create
fetch_kwargs = {} fetch_kwargs = {}
if resource_response_key is not None: if resource_response_key is not None:
fetch_kwargs = \ fetch_kwargs = {'resource_response_key': resource_response_key}
{'resource_response_key': resource_response_key}
return self.fetch(session, **fetch_kwargs) return self.fetch(session, **fetch_kwargs)
return self return self
@ -1681,7 +1674,8 @@ class Resource(dict):
raise exceptions.MethodNotSupported(self, 'fetch') raise exceptions.MethodNotSupported(self, 'fetch')
request = self._prepare_request( request = self._prepare_request(
requires_id=requires_id, base_path=base_path, requires_id=requires_id,
base_path=base_path,
) )
session = self._get_session(session) session = self._get_session(session)
if microversion is None: if microversion is None:
@ -1931,8 +1925,9 @@ class Resource(dict):
retry_on_conflict=retry_on_conflict, retry_on_conflict=retry_on_conflict,
) )
def delete(self, session, error_message=None, *, microversion=None, def delete(
**kwargs): self, session, error_message=None, *, microversion=None, **kwargs
):
"""Delete the remote resource based on this instance. """Delete the remote resource based on this instance.
:param session: The session to use for making this request. :param session: The session to use for making this request.
@ -1948,8 +1943,9 @@ class Resource(dict):
the resource was not found. the resource was not found.
""" """
response = self._raw_delete(session, microversion=microversion, response = self._raw_delete(
**kwargs) session, microversion=microversion, **kwargs
)
kwargs = {} kwargs = {}
if error_message: if error_message:
kwargs['error_message'] = error_message kwargs['error_message'] = error_message
@ -2116,7 +2112,8 @@ class Resource(dict):
for key in client_filters.keys(): for key in client_filters.keys():
if isinstance(client_filters[key], dict): if isinstance(client_filters[key], dict):
if not _dict_filter( if not _dict_filter(
client_filters[key], value.get(key, None)): client_filters[key], value.get(key, None)
):
filters_matched = False filters_matched = False
break break
elif value.get(key, None) != client_filters[key]: elif value.get(key, None) != client_filters[key]:
@ -2176,7 +2173,7 @@ class Resource(dict):
# Glance has a next field in the main body # Glance has a next field in the main body
next_link = next_link or data.get('next') next_link = next_link or data.get('next')
if next_link and next_link.startswith('/v'): if next_link and next_link.startswith('/v'):
next_link = next_link[next_link.find('/', 1):] next_link = next_link[next_link.find('/', 1) :]
if not next_link and 'next' in response.links: if not next_link and 'next' in response.links:
# RFC5988 specifies Link headers and requests parses them if they # RFC5988 specifies Link headers and requests parses them if they
@ -2281,8 +2278,11 @@ class Resource(dict):
**params, **params,
) )
return match.fetch(session, microversion=microversion, **params) return match.fetch(session, microversion=microversion, **params)
except (exceptions.NotFoundException, exceptions.BadRequestException, except (
exceptions.ForbiddenException): exceptions.NotFoundException,
exceptions.BadRequestException,
exceptions.ForbiddenException,
):
# NOTE(gtema): There are few places around openstack that return # NOTE(gtema): There are few places around openstack that return
# 400 if we try to GET resource and it doesn't exist. # 400 if we try to GET resource and it doesn't exist.
pass pass

View File

@ -36,7 +36,9 @@ class _ServiceDisabledProxyShim:
raise exceptions.ServiceDisabledException( raise exceptions.ServiceDisabledException(
"Service '{service_type}' is disabled because its configuration " "Service '{service_type}' is disabled because its configuration "
"could not be loaded. {reason}".format( "could not be loaded. {reason}".format(
service_type=self.service_type, reason=self.reason or '')) service_type=self.service_type, reason=self.reason or ''
)
)
class ServiceDescription: class ServiceDescription:
@ -73,9 +75,8 @@ class ServiceDescription:
""" """
self.service_type = service_type or self.service_type self.service_type = service_type or self.service_type
self.supported_versions = ( self.supported_versions = (
supported_versions supported_versions or self.supported_versions or {}
or self.supported_versions )
or {})
self.aliases = aliases or self.aliases self.aliases = aliases or self.aliases
self.all_types = [service_type] + self.aliases self.all_types = [service_type] + self.aliases
@ -135,7 +136,9 @@ class ServiceDescription:
"Failed to create a working proxy for service {service_type}: " "Failed to create a working proxy for service {service_type}: "
"{message}".format( "{message}".format(
service_type=self.service_type, service_type=self.service_type,
message=exc or "No valid endpoint was discoverable.")) message=exc or "No valid endpoint was discoverable.",
)
)
def _make_proxy(self, instance): def _make_proxy(self, instance):
"""Create a Proxy for the service in question. """Create a Proxy for the service in question.
@ -148,7 +151,8 @@ class ServiceDescription:
if not config.has_service(self.service_type): if not config.has_service(self.service_type):
return _ServiceDisabledProxyShim( return _ServiceDisabledProxyShim(
self.service_type, self.service_type,
config.get_disabled_reason(self.service_type)) config.get_disabled_reason(self.service_type),
)
# We don't know anything about this service, so the user is # We don't know anything about this service, so the user is
# explicitly just using us for a passthrough REST adapter. # explicitly just using us for a passthrough REST adapter.
@ -186,13 +190,12 @@ class ServiceDescription:
" {service_type} is not known or supported by" " {service_type} is not known or supported by"
" openstacksdk. The resulting Proxy object will only" " openstacksdk. The resulting Proxy object will only"
" have direct passthrough REST capabilities.".format( " have direct passthrough REST capabilities.".format(
version=version_string, version=version_string, service_type=self.service_type
service_type=self.service_type), ),
category=exceptions.UnsupportedServiceVersion) category=exceptions.UnsupportedServiceVersion,
)
elif endpoint_override: elif endpoint_override:
temp_adapter = config.get_session_client( temp_adapter = config.get_session_client(self.service_type)
self.service_type
)
api_version = temp_adapter.get_endpoint_data().api_version api_version = temp_adapter.get_endpoint_data().api_version
proxy_class = self.supported_versions.get(str(api_version[0])) proxy_class = self.supported_versions.get(str(api_version[0]))
if proxy_class: if proxy_class:
@ -207,9 +210,10 @@ class ServiceDescription:
" is not supported by openstacksdk. The resulting Proxy" " is not supported by openstacksdk. The resulting Proxy"
" object will only have direct passthrough REST" " object will only have direct passthrough REST"
" capabilities.".format( " capabilities.".format(
version=api_version, version=api_version, service_type=self.service_type
service_type=self.service_type), ),
category=exceptions.UnsupportedServiceVersion) category=exceptions.UnsupportedServiceVersion,
)
if proxy_obj: if proxy_obj:
@ -225,7 +229,9 @@ class ServiceDescription:
raise exceptions.ServiceDiscoveryException( raise exceptions.ServiceDiscoveryException(
"Failed to create a working proxy for service " "Failed to create a working proxy for service "
"{service_type}: No endpoint data found.".format( "{service_type}: No endpoint data found.".format(
service_type=self.service_type)) service_type=self.service_type
)
)
# If we've gotten here with a proxy object it means we have # If we've gotten here with a proxy object it means we have
# an endpoint_override in place. If the catalog_url and # an endpoint_override in place. If the catalog_url and
@ -235,7 +241,8 @@ class ServiceDescription:
# so that subsequent discovery calls don't get made incorrectly. # so that subsequent discovery calls don't get made incorrectly.
if data.catalog_url != data.service_url: if data.catalog_url != data.service_url:
ep_key = '{service_type}_endpoint_override'.format( ep_key = '{service_type}_endpoint_override'.format(
service_type=self.service_type.replace('-', '_')) service_type=self.service_type.replace('-', '_')
)
config.config[ep_key] = data.service_url config.config[ep_key] = data.service_url
proxy_obj = config.get_session_client( proxy_obj = config.get_session_client(
self.service_type, self.service_type,
@ -248,16 +255,16 @@ class ServiceDescription:
if version_string: if version_string:
version_kwargs['version'] = version_string version_kwargs['version'] = version_string
else: else:
supported_versions = sorted([ supported_versions = sorted(
int(f) for f in self.supported_versions]) [int(f) for f in self.supported_versions]
)
version_kwargs['min_version'] = str(supported_versions[0]) version_kwargs['min_version'] = str(supported_versions[0])
version_kwargs['max_version'] = '{version}.latest'.format( version_kwargs['max_version'] = '{version}.latest'.format(
version=str(supported_versions[-1])) version=str(supported_versions[-1])
)
temp_adapter = config.get_session_client( temp_adapter = config.get_session_client(
self.service_type, self.service_type, allow_version_hack=True, **version_kwargs
allow_version_hack=True,
**version_kwargs
) )
found_version = temp_adapter.get_api_major_version() found_version = temp_adapter.get_api_major_version()
if found_version is None: if found_version is None:
@ -268,14 +275,18 @@ class ServiceDescription:
" exists but does not have any supported versions.".format( " exists but does not have any supported versions.".format(
service_type=self.service_type, service_type=self.service_type,
cloud=instance.name, cloud=instance.name,
region_name=region_name)) region_name=region_name,
)
)
else: else:
raise exceptions.NotSupported( raise exceptions.NotSupported(
"The {service_type} service for {cloud}:{region_name}" "The {service_type} service for {cloud}:{region_name}"
" exists but no version was discoverable.".format( " exists but no version was discoverable.".format(
service_type=self.service_type, service_type=self.service_type,
cloud=instance.name, cloud=instance.name,
region_name=region_name)) region_name=region_name,
)
)
proxy_class = self.supported_versions.get(str(found_version[0])) proxy_class = self.supported_versions.get(str(found_version[0]))
if proxy_class: if proxy_class:
return config.get_session_client( return config.get_session_client(
@ -294,8 +305,10 @@ class ServiceDescription:
"Service {service_type} has no discoverable version." "Service {service_type} has no discoverable version."
" The resulting Proxy object will only have direct" " The resulting Proxy object will only have direct"
" passthrough REST capabilities.".format( " passthrough REST capabilities.".format(
service_type=self.service_type), service_type=self.service_type
category=exceptions.UnsupportedServiceVersion) ),
category=exceptions.UnsupportedServiceVersion,
)
return temp_adapter return temp_adapter
def __set__(self, instance, value): def __set__(self, instance, value):

View File

@ -47,7 +47,9 @@ class TestCase(base.BaseTestCase):
test_timeout = int(test_timeout * self.TIMEOUT_SCALING_FACTOR) test_timeout = int(test_timeout * self.TIMEOUT_SCALING_FACTOR)
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable( fixtures.EnvironmentVariable(
'OS_TEST_TIMEOUT', str(test_timeout))) 'OS_TEST_TIMEOUT', str(test_timeout)
)
)
except ValueError: except ValueError:
# Let oslotest do its thing # Let oslotest do its thing
pass pass
@ -90,7 +92,8 @@ class TestCase(base.BaseTestCase):
if isinstance(second, utils.Munch): if isinstance(second, utils.Munch):
second = second.toDict() second = second.toDict()
return super(TestCase, self).assertEqual( return super(TestCase, self).assertEqual(
first, second, *args, **kwargs) first, second, *args, **kwargs
)
def printLogs(self, *args): def printLogs(self, *args):
self._log_stream.seek(0) self._log_stream.seek(0)
@ -104,16 +107,18 @@ class TestCase(base.BaseTestCase):
if not x: if not x:
break break
yield x.encode('utf8') yield x.encode('utf8')
content = testtools.content.content_from_reader( content = testtools.content.content_from_reader(
reader, reader, testtools.content_type.UTF8_TEXT, False
testtools.content_type.UTF8_TEXT, )
False)
self.addDetail('logging', content) self.addDetail('logging', content)
def add_info_on_exception(self, name, text): def add_info_on_exception(self, name, text):
def add_content(unused): def add_content(unused):
self.addDetail(name, testtools.content.text_content( self.addDetail(
pprint.pformat(text))) name, testtools.content.text_content(pprint.pformat(text))
)
self.addOnException(add_content) self.addOnException(add_content)
def assertSubdict(self, part, whole): def assertSubdict(self, part, whole):
@ -124,11 +129,18 @@ class TestCase(base.BaseTestCase):
if not whole[key] and part[key]: if not whole[key] and part[key]:
missing_keys.append(key) missing_keys.append(key)
if missing_keys: if missing_keys:
self.fail("Keys %s are in %s but not in %s" % self.fail(
(missing_keys, part, whole)) "Keys %s are in %s but not in %s" % (missing_keys, part, whole)
wrong_values = [(key, part[key], whole[key]) )
for key in part if part[key] != whole[key]] wrong_values = [
(key, part[key], whole[key])
for key in part
if part[key] != whole[key]
]
if wrong_values: if wrong_values:
self.fail("Mismatched values: %s" % self.fail(
", ".join("for %s got %s and %s" % tpl "Mismatched values: %s"
for tpl in wrong_values)) % ", ".join(
"for %s got %s and %s" % tpl for tpl in wrong_values
)
)

View File

@ -32,7 +32,8 @@ CHOCOLATE_FLAVOR_ID = u'0c1d9008-f546-4608-9e8f-f8bdaec8ddde'
STRAWBERRY_FLAVOR_ID = u'0c1d9008-f546-4608-9e8f-f8bdaec8dddf' STRAWBERRY_FLAVOR_ID = u'0c1d9008-f546-4608-9e8f-f8bdaec8dddf'
COMPUTE_ENDPOINT = 'https://compute.example.com/v2.1' COMPUTE_ENDPOINT = 'https://compute.example.com/v2.1'
ORCHESTRATION_ENDPOINT = 'https://orchestration.example.com/v1/{p}'.format( ORCHESTRATION_ENDPOINT = 'https://orchestration.example.com/v1/{p}'.format(
p=PROJECT_ID) p=PROJECT_ID
)
NO_MD5 = '93b885adfe0da089cdf634904fd59f71' NO_MD5 = '93b885adfe0da089cdf634904fd59f71'
NO_SHA256 = '6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d' NO_SHA256 = '6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d'
FAKE_PUBLIC_KEY = ( FAKE_PUBLIC_KEY = (
@ -41,7 +42,8 @@ FAKE_PUBLIC_KEY = (
"sZacm0cZNuL69EObEGHdprfGJQajrpz22NQoCD8TFB8Wv+8om9NH9Le6s+WPe98WC77KLw8qg" "sZacm0cZNuL69EObEGHdprfGJQajrpz22NQoCD8TFB8Wv+8om9NH9Le6s+WPe98WC77KLw8qg"
"fQsbIey+JawPWl4O67ZdL5xrypuRjfIPWjgy/VH85IXg/Z/GONZ2nxHgSShMkwqSFECAC5L3P" "fQsbIey+JawPWl4O67ZdL5xrypuRjfIPWjgy/VH85IXg/Z/GONZ2nxHgSShMkwqSFECAC5L3P"
"HB+0+/12M/iikdatFSVGjpuHvkLOs3oe7m6HlOfluSJ85BzLWBbvva93qkGmLg4ZAc8rPh2O+" "HB+0+/12M/iikdatFSVGjpuHvkLOs3oe7m6HlOfluSJ85BzLWBbvva93qkGmLg4ZAc8rPh2O+"
"YIsBUHNLLMM/oQp Generated-by-Nova\n") "YIsBUHNLLMM/oQp Generated-by-Nova\n"
)
def make_fake_flavor(flavor_id, name, ram=100, disk=1600, vcpus=24): def make_fake_flavor(flavor_id, name, ram=100, disk=1600, vcpus=24):
@ -50,29 +52,36 @@ def make_fake_flavor(flavor_id, name, ram=100, disk=1600, vcpus=24):
u'OS-FLV-EXT-DATA:ephemeral': 0, u'OS-FLV-EXT-DATA:ephemeral': 0,
u'disk': disk, u'disk': disk,
u'id': flavor_id, u'id': flavor_id,
u'links': [{ u'links': [
u'href': u'{endpoint}/flavors/{id}'.format( {
endpoint=COMPUTE_ENDPOINT, id=flavor_id), u'href': u'{endpoint}/flavors/{id}'.format(
u'rel': u'self' endpoint=COMPUTE_ENDPOINT, id=flavor_id
}, { ),
u'href': u'{endpoint}/flavors/{id}'.format( u'rel': u'self',
endpoint=COMPUTE_ENDPOINT, id=flavor_id), },
u'rel': u'bookmark' {
}], u'href': u'{endpoint}/flavors/{id}'.format(
endpoint=COMPUTE_ENDPOINT, id=flavor_id
),
u'rel': u'bookmark',
},
],
u'name': name, u'name': name,
u'os-flavor-access:is_public': True, u'os-flavor-access:is_public': True,
u'ram': ram, u'ram': ram,
u'rxtx_factor': 1.0, u'rxtx_factor': 1.0,
u'swap': u'', u'swap': u'',
u'vcpus': vcpus u'vcpus': vcpus,
} }
FAKE_FLAVOR = make_fake_flavor(FLAVOR_ID, 'vanilla') FAKE_FLAVOR = make_fake_flavor(FLAVOR_ID, 'vanilla')
FAKE_CHOCOLATE_FLAVOR = make_fake_flavor( FAKE_CHOCOLATE_FLAVOR = make_fake_flavor(
CHOCOLATE_FLAVOR_ID, 'chocolate', ram=200) CHOCOLATE_FLAVOR_ID, 'chocolate', ram=200
)
FAKE_STRAWBERRY_FLAVOR = make_fake_flavor( FAKE_STRAWBERRY_FLAVOR = make_fake_flavor(
STRAWBERRY_FLAVOR_ID, 'strawberry', ram=300) STRAWBERRY_FLAVOR_ID, 'strawberry', ram=300
)
FAKE_FLAVOR_LIST = [FAKE_FLAVOR, FAKE_CHOCOLATE_FLAVOR, FAKE_STRAWBERRY_FLAVOR] FAKE_FLAVOR_LIST = [FAKE_FLAVOR, FAKE_CHOCOLATE_FLAVOR, FAKE_STRAWBERRY_FLAVOR]
FAKE_TEMPLATE = '''heat_template_version: 2014-10-16 FAKE_TEMPLATE = '''heat_template_version: 2014-10-16
@ -95,8 +104,14 @@ FAKE_TEMPLATE_CONTENT = template_format.parse(FAKE_TEMPLATE)
def make_fake_server( def make_fake_server(
server_id, name, status='ACTIVE', admin_pass=None, server_id,
addresses=None, image=None, flavor=None): name,
status='ACTIVE',
admin_pass=None,
addresses=None,
image=None,
flavor=None,
):
if addresses is None: if addresses is None:
if status == 'ACTIVE': if status == 'ACTIVE':
addresses = { addresses = {
@ -105,25 +120,28 @@ def make_fake_server(
"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:df:b0:8d", "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:df:b0:8d",
"version": 6, "version": 6,
"addr": "fddb:b018:307:0:f816:3eff:fedf:b08d", "addr": "fddb:b018:307:0:f816:3eff:fedf:b08d",
"OS-EXT-IPS:type": "fixed"}, "OS-EXT-IPS:type": "fixed",
},
{ {
"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:df:b0:8d", "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:df:b0:8d",
"version": 4, "version": 4,
"addr": "10.1.0.9", "addr": "10.1.0.9",
"OS-EXT-IPS:type": "fixed"}, "OS-EXT-IPS:type": "fixed",
},
{ {
"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:df:b0:8d", "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:df:b0:8d",
"version": 4, "version": 4,
"addr": "172.24.5.5", "addr": "172.24.5.5",
"OS-EXT-IPS:type": "floating"}]} "OS-EXT-IPS:type": "floating",
},
]
}
else: else:
addresses = {} addresses = {}
if image is None: if image is None:
image = {"id": "217f3ab1-03e0-4450-bf27-63d52b421e9e", image = {"id": "217f3ab1-03e0-4450-bf27-63d52b421e9e", "links": []}
"links": []}
if flavor is None: if flavor is None:
flavor = {"id": "64", flavor = {"id": "64", "links": []}
"links": []}
server = { server = {
"OS-EXT-STS:task_state": None, "OS-EXT-STS:task_state": None,
@ -152,7 +170,8 @@ def make_fake_server(
"created": "2017-03-23T23:57:12Z", "created": "2017-03-23T23:57:12Z",
"tenant_id": PROJECT_ID, "tenant_id": PROJECT_ID,
"os-extended-volumes:volumes_attached": [], "os-extended-volumes:volumes_attached": [],
"config_drive": "True"} "config_drive": "True",
}
if admin_pass: if admin_pass:
server['adminPass'] = admin_pass server['adminPass'] = admin_pass
return json.loads(json.dumps(server)) return json.loads(json.dumps(server))
@ -188,7 +207,8 @@ def make_fake_stack(id, name, description=None, status='CREATE_COMPLETE'):
def make_fake_stack_event( def make_fake_stack_event(
id, name, status='CREATE_COMPLETED', resource_name='id'): id, name, status='CREATE_COMPLETED', resource_name='id'
):
event_id = uuid.uuid4().hex event_id = uuid.uuid4().hex
self_url = "{endpoint}/stacks/{name}/{id}/resources/{name}/events/{event}" self_url = "{endpoint}/stacks/{name}/{id}/resources/{name}/events/{event}"
resource_url = "{endpoint}/stacks/{name}/{id}/resources/{name}" resource_url = "{endpoint}/stacks/{name}/{id}/resources/{name}"
@ -199,19 +219,25 @@ def make_fake_stack_event(
{ {
"href": self_url.format( "href": self_url.format(
endpoint=ORCHESTRATION_ENDPOINT, endpoint=ORCHESTRATION_ENDPOINT,
name=name, id=id, event=event_id), name=name,
"rel": "self" id=id,
}, { event=event_id,
),
"rel": "self",
},
{
"href": resource_url.format( "href": resource_url.format(
endpoint=ORCHESTRATION_ENDPOINT, endpoint=ORCHESTRATION_ENDPOINT, name=name, id=id
name=name, id=id), ),
"rel": "resource" "rel": "resource",
}, { },
{
"href": "{endpoint}/stacks/{name}/{id}".format( "href": "{endpoint}/stacks/{name}/{id}".format(
endpoint=ORCHESTRATION_ENDPOINT, endpoint=ORCHESTRATION_ENDPOINT, name=name, id=id
name=name, id=id), ),
"rel": "stack" "rel": "stack",
}], },
],
"logical_resource_id": name, "logical_resource_id": name,
"resource_status": status, "resource_status": status,
"resource_status_reason": "", "resource_status_reason": "",
@ -221,10 +247,14 @@ def make_fake_stack_event(
def make_fake_image( def make_fake_image(
image_id=None, md5=NO_MD5, sha256=NO_SHA256, status='active', image_id=None,
image_name=u'fake_image', md5=NO_MD5,
data=None, sha256=NO_SHA256,
checksum=u'ee36e35a297980dee1b514de9803ec6d'): status='active',
image_name=u'fake_image',
data=None,
checksum=u'ee36e35a297980dee1b514de9803ec6d',
):
if data: if data:
md5 = utils.md5(usedforsecurity=False) md5 = utils.md5(usedforsecurity=False)
sha256 = hashlib.sha256() sha256 = hashlib.sha256()
@ -249,9 +279,9 @@ def make_fake_image(
u'status': status, u'status': status,
u'tags': [], u'tags': [],
u'visibility': u'private', u'visibility': u'private',
u'locations': [{ u'locations': [
u'url': u'http://127.0.0.1/images/' + image_id, {u'url': u'http://127.0.0.1/images/' + image_id, u'metadata': {}}
u'metadata': {}}], ],
u'min_disk': 40, u'min_disk': 40,
u'virtual_size': None, u'virtual_size': None,
u'name': image_name, u'name': image_name,
@ -260,16 +290,16 @@ def make_fake_image(
u'owner_specified.openstack.md5': md5 or NO_MD5, u'owner_specified.openstack.md5': md5 or NO_MD5,
u'owner_specified.openstack.sha256': sha256 or NO_SHA256, u'owner_specified.openstack.sha256': sha256 or NO_SHA256,
u'owner_specified.openstack.object': 'images/{name}'.format( u'owner_specified.openstack.object': 'images/{name}'.format(
name=image_name), name=image_name
u'protected': False} ),
u'protected': False,
}
def make_fake_machine(machine_name, machine_id=None): def make_fake_machine(machine_name, machine_id=None):
if not machine_id: if not machine_id:
machine_id = uuid.uuid4().hex machine_id = uuid.uuid4().hex
return meta.obj_to_munch(FakeMachine( return meta.obj_to_munch(FakeMachine(id=machine_id, name=machine_name))
id=machine_id,
name=machine_name))
def make_fake_port(address, node_id=None, port_id=None): def make_fake_port(address, node_id=None, port_id=None):
@ -277,10 +307,9 @@ def make_fake_port(address, node_id=None, port_id=None):
node_id = uuid.uuid4().hex node_id = uuid.uuid4().hex
if not port_id: if not port_id:
port_id = uuid.uuid4().hex port_id = uuid.uuid4().hex
return meta.obj_to_munch(FakeMachinePort( return meta.obj_to_munch(
id=port_id, FakeMachinePort(id=port_id, address=address, node_id=node_id)
address=address, )
node_id=node_id))
class FakeFloatingIP: class FakeFloatingIP:
@ -293,63 +322,58 @@ class FakeFloatingIP:
def make_fake_server_group(id, name, policies): def make_fake_server_group(id, name, policies):
return json.loads(json.dumps({ return json.loads(
'id': id, json.dumps(
'name': name, {
'policies': policies, 'id': id,
'members': [], 'name': name,
'metadata': {}, 'policies': policies,
})) 'members': [],
'metadata': {},
}
)
)
def make_fake_hypervisor(id, name): def make_fake_hypervisor(id, name):
return json.loads(json.dumps({ return json.loads(
'id': id, json.dumps(
'hypervisor_hostname': name, {
'state': 'up', 'id': id,
'status': 'enabled', 'hypervisor_hostname': name,
"cpu_info": { 'state': 'up',
"arch": "x86_64", 'status': 'enabled',
"model": "Nehalem", "cpu_info": {
"vendor": "Intel", "arch": "x86_64",
"features": [ "model": "Nehalem",
"pge", "vendor": "Intel",
"clflush" "features": ["pge", "clflush"],
], "topology": {"cores": 1, "threads": 1, "sockets": 4},
"topology": { },
"cores": 1, "current_workload": 0,
"threads": 1, "status": "enabled",
"sockets": 4 "state": "up",
"disk_available_least": 0,
"host_ip": "1.1.1.1",
"free_disk_gb": 1028,
"free_ram_mb": 7680,
"hypervisor_type": "fake",
"hypervisor_version": 1000,
"local_gb": 1028,
"local_gb_used": 0,
"memory_mb": 8192,
"memory_mb_used": 512,
"running_vms": 0,
"service": {"host": "host1", "id": 7, "disabled_reason": None},
"vcpus": 1,
"vcpus_used": 0,
} }
}, )
"current_workload": 0, )
"status": "enabled",
"state": "up",
"disk_available_least": 0,
"host_ip": "1.1.1.1",
"free_disk_gb": 1028,
"free_ram_mb": 7680,
"hypervisor_type": "fake",
"hypervisor_version": 1000,
"local_gb": 1028,
"local_gb_used": 0,
"memory_mb": 8192,
"memory_mb_used": 512,
"running_vms": 0,
"service": {
"host": "host1",
"id": 7,
"disabled_reason": None
},
"vcpus": 1,
"vcpus_used": 0
}))
class FakeVolume: class FakeVolume:
def __init__( def __init__(self, id, status, name, attachments=[], size=75):
self, id, status, name, attachments=[],
size=75):
self.id = id self.id = id
self.status = status self.status = status
self.name = name self.name = name
@ -366,8 +390,7 @@ class FakeVolume:
class FakeVolumeSnapshot: class FakeVolumeSnapshot:
def __init__( def __init__(self, id, status, name, description, size=75):
self, id, status, name, description, size=75):
self.id = id self.id = id
self.status = status self.status = status
self.name = name self.name = name
@ -380,10 +403,20 @@ class FakeVolumeSnapshot:
class FakeMachine: class FakeMachine:
def __init__(self, id, name=None, driver=None, driver_info=None, def __init__(
chassis_uuid=None, instance_info=None, instance_uuid=None, self,
properties=None, reservation=None, last_error=None, id,
provision_state='available'): name=None,
driver=None,
driver_info=None,
chassis_uuid=None,
instance_info=None,
instance_uuid=None,
properties=None,
reservation=None,
last_error=None,
provision_state='available',
):
self.uuid = id self.uuid = id
self.name = name self.name = name
self.driver = driver self.driver = driver
@ -405,50 +438,69 @@ class FakeMachinePort:
def make_fake_neutron_security_group( def make_fake_neutron_security_group(
id, name, description, rules, stateful=True, project_id=None): id, name, description, rules, stateful=True, project_id=None
):
if not rules: if not rules:
rules = [] rules = []
if not project_id: if not project_id:
project_id = PROJECT_ID project_id = PROJECT_ID
return json.loads(json.dumps({ return json.loads(
'id': id, json.dumps(
'name': name, {
'description': description, 'id': id,
'stateful': stateful, 'name': name,
'project_id': project_id, 'description': description,
'tenant_id': project_id, 'stateful': stateful,
'security_group_rules': rules, 'project_id': project_id,
})) 'tenant_id': project_id,
'security_group_rules': rules,
}
)
)
def make_fake_nova_security_group_rule( def make_fake_nova_security_group_rule(
id, from_port, to_port, ip_protocol, cidr): id, from_port, to_port, ip_protocol, cidr
return json.loads(json.dumps({ ):
'id': id, return json.loads(
'from_port': int(from_port), json.dumps(
'to_port': int(to_port), {
'ip_protcol': 'tcp', 'id': id,
'ip_range': { 'from_port': int(from_port),
'cidr': cidr 'to_port': int(to_port),
} 'ip_protcol': 'tcp',
})) 'ip_range': {'cidr': cidr},
}
)
)
def make_fake_nova_security_group(id, name, description, rules): def make_fake_nova_security_group(id, name, description, rules):
if not rules: if not rules:
rules = [] rules = []
return json.loads(json.dumps({ return json.loads(
'id': id, json.dumps(
'name': name, {
'description': description, 'id': id,
'tenant_id': PROJECT_ID, 'name': name,
'rules': rules, 'description': description,
})) 'tenant_id': PROJECT_ID,
'rules': rules,
}
)
)
class FakeNovaSecgroupRule: class FakeNovaSecgroupRule:
def __init__(self, id, from_port=None, to_port=None, ip_protocol=None, def __init__(
cidr=None, parent_group_id=None): self,
id,
from_port=None,
to_port=None,
ip_protocol=None,
cidr=None,
parent_group_id=None,
):
self.id = id self.id = id
self.from_port = from_port self.from_port = from_port
self.to_port = to_port self.to_port = to_port
@ -465,8 +517,7 @@ class FakeHypervisor:
class FakeZone: class FakeZone:
def __init__(self, id, name, type_, email, description, def __init__(self, id, name, type_, email, description, ttl, masters):
ttl, masters):
self.id = id self.id = id
self.name = name self.name = name
self.type_ = type_ self.type_ = type_
@ -477,8 +528,7 @@ class FakeZone:
class FakeRecordset: class FakeRecordset:
def __init__(self, zone, id, name, type_, description, def __init__(self, zone, id, name, type_, description, ttl, records):
ttl, records):
self.zone = zone self.zone = zone
self.id = id self.id = id
self.name = name self.name = name
@ -488,22 +538,27 @@ class FakeRecordset:
self.records = records self.records = records
def make_fake_aggregate(id, name, availability_zone='nova', def make_fake_aggregate(
metadata=None, hosts=None): id, name, availability_zone='nova', metadata=None, hosts=None
):
if not metadata: if not metadata:
metadata = {} metadata = {}
if not hosts: if not hosts:
hosts = [] hosts = []
return json.loads(json.dumps({ return json.loads(
"availability_zone": availability_zone, json.dumps(
"created_at": datetime.datetime.now().isoformat(), {
"deleted": False, "availability_zone": availability_zone,
"deleted_at": None, "created_at": datetime.datetime.now().isoformat(),
"hosts": hosts, "deleted": False,
"id": int(id), "deleted_at": None,
"metadata": { "hosts": hosts,
"availability_zone": availability_zone, "id": int(id),
}, "metadata": {
"name": name, "availability_zone": availability_zone,
"updated_at": None, },
})) "name": name,
"updated_at": None,
}
)
)

View File

@ -51,10 +51,12 @@ class BaseFunctionalTest(base.TestCase):
self._demo_name = os.environ.get('OPENSTACKSDK_DEMO_CLOUD', 'devstack') self._demo_name = os.environ.get('OPENSTACKSDK_DEMO_CLOUD', 'devstack')
self._demo_name_alt = os.environ.get( self._demo_name_alt = os.environ.get(
'OPENSTACKSDK_DEMO_CLOUD_ALT', 'devstack-alt', 'OPENSTACKSDK_DEMO_CLOUD_ALT',
'devstack-alt',
) )
self._op_name = os.environ.get( self._op_name = os.environ.get(
'OPENSTACKSDK_OPERATOR_CLOUD', 'devstack-admin', 'OPENSTACKSDK_OPERATOR_CLOUD',
'devstack-admin',
) )
self.config = openstack.config.OpenStackConfig() self.config = openstack.config.OpenStackConfig()
@ -64,8 +66,9 @@ class BaseFunctionalTest(base.TestCase):
else: else:
self.operator_cloud = None self.operator_cloud = None
self.identity_version = \ self.identity_version = self.user_cloud.config.get_api_version(
self.user_cloud.config.get_api_version('identity') 'identity'
)
self.flavor = self._pick_flavor() self.flavor = self._pick_flavor()
self.image = self._pick_image() self.image = self._pick_image()
@ -73,8 +76,11 @@ class BaseFunctionalTest(base.TestCase):
# Defines default timeout for wait_for methods used # Defines default timeout for wait_for methods used
# in the functional tests # in the functional tests
self._wait_for_timeout = int( self._wait_for_timeout = int(
os.getenv(self._wait_for_timeout_key, os.getenv( os.getenv(
'OPENSTACKSDK_FUNC_TEST_TIMEOUT', 300))) self._wait_for_timeout_key,
os.getenv('OPENSTACKSDK_FUNC_TEST_TIMEOUT', 300),
)
)
def _set_user_cloud(self, **kwargs): def _set_user_cloud(self, **kwargs):
user_config = self.config.get_one(cloud=self._demo_name, **kwargs) user_config = self.config.get_one(cloud=self._demo_name, **kwargs)
@ -85,7 +91,8 @@ class BaseFunctionalTest(base.TestCase):
# it # it
if self._demo_name_alt: if self._demo_name_alt:
user_config_alt = self.config.get_one( user_config_alt = self.config.get_one(
cloud=self._demo_name_alt, **kwargs) cloud=self._demo_name_alt, **kwargs
)
self.user_cloud_alt = connection.Connection(config=user_config_alt) self.user_cloud_alt = connection.Connection(config=user_config_alt)
_disable_keep_alive(self.user_cloud_alt) _disable_keep_alive(self.user_cloud_alt)
else: else:
@ -119,7 +126,8 @@ class BaseFunctionalTest(base.TestCase):
return flavor return flavor
raise self.failureException( raise self.failureException(
"Cloud does not have flavor '%s'", flavor_name, "Cloud does not have flavor '%s'",
flavor_name,
) )
# Enable running functional tests against RAX, which requires # Enable running functional tests against RAX, which requires
@ -159,7 +167,8 @@ class BaseFunctionalTest(base.TestCase):
return image return image
raise self.failureException( raise self.failureException(
"Cloud does not have image '%s'", image_name, "Cloud does not have image '%s'",
image_name,
) )
for image in images: for image in images:
@ -186,6 +195,7 @@ class BaseFunctionalTest(base.TestCase):
def cleanup(): def cleanup():
result = func(*args, **kwargs) result = func(*args, **kwargs)
self.assertIsNone(result) self.assertIsNone(result)
self.addCleanup(cleanup) self.addCleanup(cleanup)
def require_service(self, service_type, min_microversion=None, **kwargs): def require_service(self, service_type, min_microversion=None, **kwargs):
@ -201,14 +211,18 @@ class BaseFunctionalTest(base.TestCase):
:returns: True if the service exists, otherwise False. :returns: True if the service exists, otherwise False.
""" """
if not self.conn.has_service(service_type): if not self.conn.has_service(service_type):
self.skipTest('Service {service_type} not found in cloud'.format( self.skipTest(
service_type=service_type)) 'Service {service_type} not found in cloud'.format(
service_type=service_type
)
)
if not min_microversion: if not min_microversion:
return return
data = self.conn.session.get_endpoint_data( data = self.conn.session.get_endpoint_data(
service_type=service_type, **kwargs) service_type=service_type, **kwargs
)
if not ( if not (
data.min_microversion data.min_microversion
@ -230,12 +244,11 @@ class BaseFunctionalTest(base.TestCase):
# unix_t is also used to easier determine orphans when running real # unix_t is also used to easier determine orphans when running real
# functional tests on a real cloud # functional tests on a real cloud
return (prefix if prefix else '') + "{time}-{uuid}".format( return (prefix if prefix else '') + "{time}-{uuid}".format(
time=int(time.time()), time=int(time.time()), uuid=uuid.uuid4().hex
uuid=uuid.uuid4().hex) )
class KeystoneBaseFunctionalTest(BaseFunctionalTest): class KeystoneBaseFunctionalTest(BaseFunctionalTest):
def setUp(self): def setUp(self):
super(KeystoneBaseFunctionalTest, self).setUp() super(KeystoneBaseFunctionalTest, self).setUp()

View File

@ -37,44 +37,49 @@ from openstack.tests import fakes
_ProjectData = collections.namedtuple( _ProjectData = collections.namedtuple(
'ProjectData', 'ProjectData',
'project_id, project_name, enabled, domain_id, description, ' 'project_id, project_name, enabled, domain_id, description, '
'parent_id, json_response, json_request') 'parent_id, json_response, json_request',
)
_UserData = collections.namedtuple( _UserData = collections.namedtuple(
'UserData', 'UserData',
'user_id, password, name, email, description, domain_id, enabled, ' 'user_id, password, name, email, description, domain_id, enabled, '
'json_response, json_request') 'json_response, json_request',
)
_GroupData = collections.namedtuple( _GroupData = collections.namedtuple(
'GroupData', 'GroupData',
'group_id, group_name, domain_id, description, json_response, ' 'group_id, group_name, domain_id, description, json_response, '
'json_request') 'json_request',
)
_DomainData = collections.namedtuple( _DomainData = collections.namedtuple(
'DomainData', 'DomainData',
'domain_id, domain_name, description, json_response, ' 'domain_id, domain_name, description, json_response, ' 'json_request',
'json_request') )
_ServiceData = collections.namedtuple( _ServiceData = collections.namedtuple(
'Servicedata', 'Servicedata',
'service_id, service_name, service_type, description, enabled, ' 'service_id, service_name, service_type, description, enabled, '
'json_response_v3, json_response_v2, json_request') 'json_response_v3, json_response_v2, json_request',
)
_EndpointDataV3 = collections.namedtuple( _EndpointDataV3 = collections.namedtuple(
'EndpointData', 'EndpointData',
'endpoint_id, service_id, interface, region_id, url, enabled, ' 'endpoint_id, service_id, interface, region_id, url, enabled, '
'json_response, json_request') 'json_response, json_request',
)
# NOTE(notmorgan): Shade does not support domain-specific roles # NOTE(notmorgan): Shade does not support domain-specific roles
# This should eventually be fixed if it becomes a main-stream feature. # This should eventually be fixed if it becomes a main-stream feature.
_RoleData = collections.namedtuple( _RoleData = collections.namedtuple(
'RoleData', 'RoleData', 'role_id, role_name, json_response, json_request'
'role_id, role_name, json_response, json_request') )
class TestCase(base.TestCase): class TestCase(base.TestCase):
@ -92,17 +97,20 @@ class TestCase(base.TestCase):
def _nosleep(seconds): def _nosleep(seconds):
return realsleep(seconds * 0.0001) return realsleep(seconds * 0.0001)
self.sleep_fixture = self.useFixture(fixtures.MonkeyPatch( self.sleep_fixture = self.useFixture(
'time.sleep', fixtures.MonkeyPatch('time.sleep', _nosleep)
_nosleep)) )
self.fixtures_directory = 'openstack/tests/unit/fixtures' self.fixtures_directory = 'openstack/tests/unit/fixtures'
self.os_fixture = self.useFixture( self.os_fixture = self.useFixture(
os_fixture.ConnectionFixture(project_id=fakes.PROJECT_ID)) os_fixture.ConnectionFixture(project_id=fakes.PROJECT_ID)
)
# Isolate openstack.config from test environment # Isolate openstack.config from test environment
config = tempfile.NamedTemporaryFile(delete=False) config = tempfile.NamedTemporaryFile(delete=False)
cloud_path = '%s/clouds/%s' % (self.fixtures_directory, cloud_path = '%s/clouds/%s' % (
cloud_config_fixture) self.fixtures_directory,
cloud_config_fixture,
)
with open(cloud_path, 'rb') as f: with open(cloud_path, 'rb') as f:
content = f.read() content = f.read()
config.write(content) config.write(content)
@ -115,7 +123,8 @@ class TestCase(base.TestCase):
self.config = occ.OpenStackConfig( self.config = occ.OpenStackConfig(
config_files=[config.name], config_files=[config.name],
vendor_files=[vendor.name], vendor_files=[vendor.name],
secure_files=['non-existant']) secure_files=['non-existant'],
)
self.oslo_config_dict = { self.oslo_config_dict = {
# All defaults for nova # All defaults for nova
@ -126,7 +135,7 @@ class TestCase(base.TestCase):
'heat': { 'heat': {
'region_name': 'SpecialRegion', 'region_name': 'SpecialRegion',
'interface': 'internal', 'interface': 'internal',
'endpoint_override': 'https://example.org:8888/heat/v2' 'endpoint_override': 'https://example.org:8888/heat/v2',
}, },
# test a service with dashes # test a service with dashes
'ironic_inspector': { 'ironic_inspector': {
@ -151,7 +160,8 @@ class TestCase(base.TestCase):
# request in the correct orders. # request in the correct orders.
self._uri_registry = collections.OrderedDict() self._uri_registry = collections.OrderedDict()
self.discovery_json = os.path.join( self.discovery_json = os.path.join(
self.fixtures_directory, 'discovery.json') self.fixtures_directory, 'discovery.json'
)
self.use_keystone_v3() self.use_keystone_v3()
self.__register_uris_called = False self.__register_uris_called = False
@ -166,11 +176,18 @@ class TestCase(base.TestCase):
return conf return conf
# TODO(shade) Update this to handle service type aliases # TODO(shade) Update this to handle service type aliases
def get_mock_url(self, service_type, interface='public', resource=None, def get_mock_url(
append=None, base_url_append=None, self,
qs_elements=None): service_type,
interface='public',
resource=None,
append=None,
base_url_append=None,
qs_elements=None,
):
endpoint_url = self.cloud.endpoint_for( endpoint_url = self.cloud.endpoint_for(
service_type=service_type, interface=interface) service_type=service_type, interface=interface
)
# Strip trailing slashes, so as not to produce double-slashes below # Strip trailing slashes, so as not to produce double-slashes below
if endpoint_url.endswith('/'): if endpoint_url.endswith('/'):
endpoint_url = endpoint_url[:-1] endpoint_url = endpoint_url[:-1]
@ -184,13 +201,17 @@ class TestCase(base.TestCase):
to_join.extend([urllib.parse.quote(i) for i in append]) to_join.extend([urllib.parse.quote(i) for i in append])
if qs_elements is not None: if qs_elements is not None:
qs = '?%s' % '&'.join(qs_elements) qs = '?%s' % '&'.join(qs_elements)
return '%(uri)s%(qs)s' % { return '%(uri)s%(qs)s' % {'uri': '/'.join(to_join), 'qs': qs}
'uri': '/'.join(to_join),
'qs': qs}
def mock_for_keystone_projects(self, project=None, v3=True, def mock_for_keystone_projects(
list_get=False, id_get=False, self,
project_list=None, project_count=None): project=None,
v3=True,
list_get=False,
id_get=False,
project_list=None,
project_count=None,
):
if project: if project:
assert not (project_list or project_count) assert not (project_list or project_count)
elif project_list: elif project_list:
@ -198,8 +219,9 @@ class TestCase(base.TestCase):
elif project_count: elif project_count:
assert not (project or project_list) assert not (project or project_list)
else: else:
raise Exception('Must specify a project, project_list, ' raise Exception(
'or project_count') 'Must specify a project, project_list, ' 'or project_count'
)
assert list_get or id_get assert list_get or id_get
base_url_append = 'v3' if v3 else None base_url_append = 'v3' if v3 else None
@ -207,40 +229,57 @@ class TestCase(base.TestCase):
project_list = [project] project_list = [project]
elif project_count: elif project_count:
# Generate multiple projects # Generate multiple projects
project_list = [self._get_project_data(v3=v3) project_list = [
for c in range(0, project_count)] self._get_project_data(v3=v3) for c in range(0, project_count)
]
uri_mock_list = [] uri_mock_list = []
if list_get: if list_get:
uri_mock_list.append( uri_mock_list.append(
dict(method='GET', dict(
uri=self.get_mock_url( method='GET',
service_type='identity', uri=self.get_mock_url(
interface='admin', service_type='identity',
resource='projects', interface='admin',
base_url_append=base_url_append), resource='projects',
status_code=200, base_url_append=base_url_append,
json={'projects': [p.json_response['project'] ),
for p in project_list]}) status_code=200,
json={
'projects': [
p.json_response['project'] for p in project_list
]
},
)
) )
if id_get: if id_get:
for p in project_list: for p in project_list:
uri_mock_list.append( uri_mock_list.append(
dict(method='GET', dict(
uri=self.get_mock_url( method='GET',
service_type='identity', uri=self.get_mock_url(
interface='admin', service_type='identity',
resource='projects', interface='admin',
append=[p.project_id], resource='projects',
base_url_append=base_url_append), append=[p.project_id],
status_code=200, base_url_append=base_url_append,
json=p.json_response) ),
status_code=200,
json=p.json_response,
)
) )
self.__do_register_uris(uri_mock_list) self.__do_register_uris(uri_mock_list)
return project_list return project_list
def _get_project_data(self, project_name=None, enabled=None, def _get_project_data(
domain_id=None, description=None, v3=True, self,
project_id=None, parent_id=None): project_name=None,
enabled=None,
domain_id=None,
description=None,
v3=True,
project_id=None,
parent_id=None,
):
project_name = project_name or self.getUniqueString('projectName') project_name = project_name or self.getUniqueString('projectName')
project_id = uuid.UUID(project_id or uuid.uuid4().hex).hex project_id = uuid.UUID(project_id or uuid.uuid4().hex).hex
if parent_id: if parent_id:
@ -264,9 +303,16 @@ class TestCase(base.TestCase):
response['description'] = description response['description'] = description
request['description'] = description request['description'] = description
request.setdefault('description', None) request.setdefault('description', None)
return _ProjectData(project_id, project_name, enabled, domain_id, return _ProjectData(
description, parent_id, project_id,
{'project': response}, {'project': request}) project_name,
enabled,
domain_id,
description,
parent_id,
{'project': response},
{'project': request},
)
def _get_group_data(self, name=None, domain_id=None, description=None): def _get_group_data(self, name=None, domain_id=None, description=None):
group_id = uuid.uuid4().hex group_id = uuid.uuid4().hex
@ -278,8 +324,14 @@ class TestCase(base.TestCase):
response['description'] = description response['description'] = description
request['description'] = description request['description'] = description
return _GroupData(group_id, name, domain_id, description, return _GroupData(
{'group': response}, {'group': request}) group_id,
name,
domain_id,
description,
{'group': response},
{'group': request},
)
def _get_user_data(self, name=None, password=None, **kwargs): def _get_user_data(self, name=None, password=None, **kwargs):
@ -305,16 +357,27 @@ class TestCase(base.TestCase):
if response['description']: if response['description']:
request['description'] = response['description'] request['description'] = response['description']
self.assertIs(0, len(kwargs), message='extra key-word args received ' self.assertIs(
'on _get_user_data') 0,
len(kwargs),
message='extra key-word args received ' 'on _get_user_data',
)
return _UserData(user_id, password, name, response['email'], return _UserData(
response['description'], response.get('domain_id'), user_id,
response.get('enabled'), {'user': response}, password,
{'user': request}) name,
response['email'],
response['description'],
response.get('domain_id'),
response.get('enabled'),
{'user': response},
{'user': request},
)
def _get_domain_data(self, domain_name=None, description=None, def _get_domain_data(
enabled=None): self, domain_name=None, description=None, enabled=None
):
domain_id = uuid.uuid4().hex domain_id = uuid.uuid4().hex
domain_name = domain_name or self.getUniqueString('domainName') domain_name = domain_name or self.getUniqueString('domainName')
response = {'id': domain_id, 'name': domain_name} response = {'id': domain_id, 'name': domain_name}
@ -326,41 +389,76 @@ class TestCase(base.TestCase):
response['description'] = description response['description'] = description
request['description'] = description request['description'] = description
response.setdefault('enabled', True) response.setdefault('enabled', True)
return _DomainData(domain_id, domain_name, description, return _DomainData(
{'domain': response}, {'domain': request}) domain_id,
domain_name,
description,
{'domain': response},
{'domain': request},
)
def _get_service_data(self, type=None, name=None, description=None, def _get_service_data(
enabled=True): self, type=None, name=None, description=None, enabled=True
):
service_id = uuid.uuid4().hex service_id = uuid.uuid4().hex
name = name or uuid.uuid4().hex name = name or uuid.uuid4().hex
type = type or uuid.uuid4().hex type = type or uuid.uuid4().hex
response = {'id': service_id, 'name': name, 'type': type, response = {
'enabled': enabled} 'id': service_id,
'name': name,
'type': type,
'enabled': enabled,
}
if description is not None: if description is not None:
response['description'] = description response['description'] = description
request = response.copy() request = response.copy()
request.pop('id') request.pop('id')
return _ServiceData(service_id, name, type, description, enabled, return _ServiceData(
{'service': response}, service_id,
{'OS-KSADM:service': response}, request) name,
type,
description,
enabled,
{'service': response},
{'OS-KSADM:service': response},
request,
)
def _get_endpoint_v3_data(self, service_id=None, region=None, def _get_endpoint_v3_data(
url=None, interface=None, enabled=True): self,
service_id=None,
region=None,
url=None,
interface=None,
enabled=True,
):
endpoint_id = uuid.uuid4().hex endpoint_id = uuid.uuid4().hex
service_id = service_id or uuid.uuid4().hex service_id = service_id or uuid.uuid4().hex
region = region or uuid.uuid4().hex region = region or uuid.uuid4().hex
url = url or 'https://example.com/' url = url or 'https://example.com/'
interface = interface or uuid.uuid4().hex interface = interface or uuid.uuid4().hex
response = {'id': endpoint_id, 'service_id': service_id, response = {
'region_id': region, 'interface': interface, 'id': endpoint_id,
'url': url, 'enabled': enabled} 'service_id': service_id,
'region_id': region,
'interface': interface,
'url': url,
'enabled': enabled,
}
request = response.copy() request = response.copy()
request.pop('id') request.pop('id')
return _EndpointDataV3(endpoint_id, service_id, interface, region, return _EndpointDataV3(
url, enabled, {'endpoint': response}, endpoint_id,
{'endpoint': request}) service_id,
interface,
region,
url,
enabled,
{'endpoint': response},
{'endpoint': request},
)
def _get_role_data(self, role_name=None): def _get_role_data(self, role_name=None):
role_id = uuid.uuid4().hex role_id = uuid.uuid4().hex
@ -368,20 +466,28 @@ class TestCase(base.TestCase):
request = {'name': role_name} request = {'name': role_name}
response = request.copy() response = request.copy()
response['id'] = role_id response['id'] = role_id
return _RoleData(role_id, role_name, {'role': response}, return _RoleData(
{'role': request}) role_id, role_name, {'role': response}, {'role': request}
)
def use_broken_keystone(self): def use_broken_keystone(self):
self.adapter = self.useFixture(rm_fixture.Fixture()) self.adapter = self.useFixture(rm_fixture.Fixture())
self.calls = [] self.calls = []
self._uri_registry.clear() self._uri_registry.clear()
self.__do_register_uris([ self.__do_register_uris(
dict(method='GET', uri='https://identity.example.com/', [
text=open(self.discovery_json, 'r').read()), dict(
dict(method='POST', method='GET',
uri='https://identity.example.com/v3/auth/tokens', uri='https://identity.example.com/',
status_code=400), text=open(self.discovery_json, 'r').read(),
]) ),
dict(
method='POST',
uri='https://identity.example.com/v3/auth/tokens',
status_code=400,
),
]
)
self._make_test_cloud(identity_api_version='3') self._make_test_cloud(identity_api_version='3')
def use_nothing(self): def use_nothing(self):
@ -389,40 +495,38 @@ class TestCase(base.TestCase):
self._uri_registry.clear() self._uri_registry.clear()
def get_keystone_v3_token( def get_keystone_v3_token(
self, self,
project_name='admin', project_name='admin',
): ):
return dict( return dict(
method='POST', method='POST',
uri='https://identity.example.com/v3/auth/tokens', uri='https://identity.example.com/v3/auth/tokens',
headers={ headers={'X-Subject-Token': self.getUniqueString('KeystoneToken')},
'X-Subject-Token': self.getUniqueString('KeystoneToken')
},
json=self.os_fixture.v3_token, json=self.os_fixture.v3_token,
validate=dict(json={ validate=dict(
'auth': { json={
'identity': { 'auth': {
'methods': ['password'], 'identity': {
'password': { 'methods': ['password'],
'user': { 'password': {
'domain': { 'user': {
'name': 'default', 'domain': {
}, 'name': 'default',
'name': 'admin', },
'password': 'password' 'name': 'admin',
} 'password': 'password',
} }
},
'scope': {
'project': {
'domain': {
'name': 'default'
}, },
'name': project_name },
} 'scope': {
'project': {
'domain': {'name': 'default'},
'name': project_name,
}
},
} }
} }
}), ),
) )
def get_keystone_discovery(self): def get_keystone_discovery(self):
@ -437,10 +541,12 @@ class TestCase(base.TestCase):
self.adapter = self.useFixture(rm_fixture.Fixture()) self.adapter = self.useFixture(rm_fixture.Fixture())
self.calls = [] self.calls = []
self._uri_registry.clear() self._uri_registry.clear()
self.__do_register_uris([ self.__do_register_uris(
self.get_keystone_discovery(), [
self.get_keystone_v3_token(), self.get_keystone_discovery(),
]) self.get_keystone_v3_token(),
]
)
self._make_test_cloud(identity_api_version='3') self._make_test_cloud(identity_api_version='3')
def use_keystone_v2(self): def use_keystone_v2(self):
@ -448,119 +554,171 @@ class TestCase(base.TestCase):
self.calls = [] self.calls = []
self._uri_registry.clear() self._uri_registry.clear()
self.__do_register_uris([ self.__do_register_uris(
self.get_keystone_discovery(), [
dict(method='POST', self.get_keystone_discovery(),
uri='https://identity.example.com/v2.0/tokens', dict(
json=self.os_fixture.v2_token, method='POST',
), uri='https://identity.example.com/v2.0/tokens',
]) json=self.os_fixture.v2_token,
),
]
)
self._make_test_cloud(cloud_name='_test_cloud_v2_', self._make_test_cloud(
identity_api_version='2.0') cloud_name='_test_cloud_v2_', identity_api_version='2.0'
)
def _make_test_cloud(self, cloud_name='_test_cloud_', **kwargs): def _make_test_cloud(self, cloud_name='_test_cloud_', **kwargs):
test_cloud = os.environ.get('OPENSTACKSDK_OS_CLOUD', cloud_name) test_cloud = os.environ.get('OPENSTACKSDK_OS_CLOUD', cloud_name)
self.cloud_config = self.config.get_one( self.cloud_config = self.config.get_one(
cloud=test_cloud, validate=True, **kwargs) cloud=test_cloud, validate=True, **kwargs
)
self.cloud = openstack.connection.Connection( self.cloud = openstack.connection.Connection(
config=self.cloud_config, strict=self.strict_cloud) config=self.cloud_config, strict=self.strict_cloud
)
def get_cinder_discovery_mock_dict( def get_cinder_discovery_mock_dict(
self, self,
block_storage_version_json='block-storage-version.json', block_storage_version_json='block-storage-version.json',
block_storage_discovery_url='https://block-storage.example.com/'): block_storage_discovery_url='https://block-storage.example.com/',
):
discovery_fixture = os.path.join( discovery_fixture = os.path.join(
self.fixtures_directory, block_storage_version_json) self.fixtures_directory, block_storage_version_json
return dict(method='GET', uri=block_storage_discovery_url, )
text=open(discovery_fixture, 'r').read()) return dict(
method='GET',
uri=block_storage_discovery_url,
text=open(discovery_fixture, 'r').read(),
)
def get_glance_discovery_mock_dict( def get_glance_discovery_mock_dict(
self, self,
image_version_json='image-version.json', image_version_json='image-version.json',
image_discovery_url='https://image.example.com/'): image_discovery_url='https://image.example.com/',
):
discovery_fixture = os.path.join( discovery_fixture = os.path.join(
self.fixtures_directory, image_version_json) self.fixtures_directory, image_version_json
return dict(method='GET', uri=image_discovery_url, )
status_code=300, return dict(
text=open(discovery_fixture, 'r').read()) method='GET',
uri=image_discovery_url,
status_code=300,
text=open(discovery_fixture, 'r').read(),
)
def get_nova_discovery_mock_dict( def get_nova_discovery_mock_dict(
self, self,
compute_version_json='compute-version.json', compute_version_json='compute-version.json',
compute_discovery_url='https://compute.example.com/v2.1/'): compute_discovery_url='https://compute.example.com/v2.1/',
):
discovery_fixture = os.path.join( discovery_fixture = os.path.join(
self.fixtures_directory, compute_version_json) self.fixtures_directory, compute_version_json
)
return dict( return dict(
method='GET', method='GET',
uri=compute_discovery_url, uri=compute_discovery_url,
text=open(discovery_fixture, 'r').read()) text=open(discovery_fixture, 'r').read(),
)
def get_placement_discovery_mock_dict( def get_placement_discovery_mock_dict(
self, discovery_fixture='placement.json'): self, discovery_fixture='placement.json'
):
discovery_fixture = os.path.join( discovery_fixture = os.path.join(
self.fixtures_directory, discovery_fixture) self.fixtures_directory, discovery_fixture
return dict(method='GET', uri="https://placement.example.com/", )
text=open(discovery_fixture, 'r').read()) return dict(
method='GET',
uri="https://placement.example.com/",
text=open(discovery_fixture, 'r').read(),
)
def get_designate_discovery_mock_dict(self): def get_designate_discovery_mock_dict(self):
discovery_fixture = os.path.join( discovery_fixture = os.path.join(self.fixtures_directory, "dns.json")
self.fixtures_directory, "dns.json") return dict(
return dict(method='GET', uri="https://dns.example.com/", method='GET',
text=open(discovery_fixture, 'r').read()) uri="https://dns.example.com/",
text=open(discovery_fixture, 'r').read(),
)
def get_ironic_discovery_mock_dict(self): def get_ironic_discovery_mock_dict(self):
discovery_fixture = os.path.join( discovery_fixture = os.path.join(
self.fixtures_directory, "baremetal.json") self.fixtures_directory, "baremetal.json"
return dict(method='GET', uri="https://baremetal.example.com/", )
text=open(discovery_fixture, 'r').read()) return dict(
method='GET',
uri="https://baremetal.example.com/",
text=open(discovery_fixture, 'r').read(),
)
def get_senlin_discovery_mock_dict(self): def get_senlin_discovery_mock_dict(self):
discovery_fixture = os.path.join( discovery_fixture = os.path.join(
self.fixtures_directory, "clustering.json") self.fixtures_directory, "clustering.json"
return dict(method='GET', uri="https://clustering.example.com/", )
text=open(discovery_fixture, 'r').read()) return dict(
method='GET',
uri="https://clustering.example.com/",
text=open(discovery_fixture, 'r').read(),
)
def use_compute_discovery( def use_compute_discovery(
self, compute_version_json='compute-version.json', self,
compute_discovery_url='https://compute.example.com/v2.1/'): compute_version_json='compute-version.json',
self.__do_register_uris([ compute_discovery_url='https://compute.example.com/v2.1/',
self.get_nova_discovery_mock_dict( ):
compute_version_json, compute_discovery_url), self.__do_register_uris(
]) [
self.get_nova_discovery_mock_dict(
compute_version_json, compute_discovery_url
),
]
)
def get_cyborg_discovery_mock_dict(self): def get_cyborg_discovery_mock_dict(self):
discovery_fixture = os.path.join( discovery_fixture = os.path.join(
self.fixtures_directory, "accelerator.json") self.fixtures_directory, "accelerator.json"
return dict(method='GET', uri="https://accelerator.example.com/", )
text=open(discovery_fixture, 'r').read()) return dict(
method='GET',
uri="https://accelerator.example.com/",
text=open(discovery_fixture, 'r').read(),
)
def get_manila_discovery_mock_dict(self): def get_manila_discovery_mock_dict(self):
discovery_fixture = os.path.join( discovery_fixture = os.path.join(
self.fixtures_directory, "shared-file-system.json") self.fixtures_directory, "shared-file-system.json"
return dict(method='GET', )
uri="https://shared-file-system.example.com/", return dict(
text=open(discovery_fixture, 'r').read()) method='GET',
uri="https://shared-file-system.example.com/",
text=open(discovery_fixture, 'r').read(),
)
def use_glance( def use_glance(
self, image_version_json='image-version.json', self,
image_discovery_url='https://image.example.com/'): image_version_json='image-version.json',
image_discovery_url='https://image.example.com/',
):
# NOTE(notmorgan): This method is only meant to be used in "setUp" # NOTE(notmorgan): This method is only meant to be used in "setUp"
# where the ordering of the url being registered is tightly controlled # where the ordering of the url being registered is tightly controlled
# if the functionality of .use_glance is meant to be used during an # if the functionality of .use_glance is meant to be used during an
# actual test case, use .get_glance_discovery_mock and apply to the # actual test case, use .get_glance_discovery_mock and apply to the
# right location in the mock_uris when calling .register_uris # right location in the mock_uris when calling .register_uris
self.__do_register_uris([ self.__do_register_uris(
self.get_glance_discovery_mock_dict( [
image_version_json, image_discovery_url)]) self.get_glance_discovery_mock_dict(
image_version_json, image_discovery_url
)
]
)
def use_cinder(self): def use_cinder(self):
self.__do_register_uris([ self.__do_register_uris([self.get_cinder_discovery_mock_dict()])
self.get_cinder_discovery_mock_dict()])
def use_placement(self, **kwargs): def use_placement(self, **kwargs):
self.__do_register_uris([ self.__do_register_uris(
self.get_placement_discovery_mock_dict(**kwargs)]) [self.get_placement_discovery_mock_dict(**kwargs)]
)
def use_designate(self): def use_designate(self):
# NOTE(slaweq): This method is only meant to be used in "setUp" # NOTE(slaweq): This method is only meant to be used in "setUp"
@ -568,8 +726,7 @@ class TestCase(base.TestCase):
# if the functionality of .use_designate is meant to be used during an # if the functionality of .use_designate is meant to be used during an
# actual test case, use .get_designate_discovery_mock and apply to the # actual test case, use .get_designate_discovery_mock and apply to the
# right location in the mock_uris when calling .register_uris # right location in the mock_uris when calling .register_uris
self.__do_register_uris([ self.__do_register_uris([self.get_designate_discovery_mock_dict()])
self.get_designate_discovery_mock_dict()])
def use_ironic(self): def use_ironic(self):
# NOTE(TheJulia): This method is only meant to be used in "setUp" # NOTE(TheJulia): This method is only meant to be used in "setUp"
@ -577,8 +734,7 @@ class TestCase(base.TestCase):
# if the functionality of .use_ironic is meant to be used during an # if the functionality of .use_ironic is meant to be used during an
# actual test case, use .get_ironic_discovery_mock and apply to the # actual test case, use .get_ironic_discovery_mock and apply to the
# right location in the mock_uris when calling .register_uris # right location in the mock_uris when calling .register_uris
self.__do_register_uris([ self.__do_register_uris([self.get_ironic_discovery_mock_dict()])
self.get_ironic_discovery_mock_dict()])
def use_senlin(self): def use_senlin(self):
# NOTE(elachance): This method is only meant to be used in "setUp" # NOTE(elachance): This method is only meant to be used in "setUp"
@ -586,8 +742,7 @@ class TestCase(base.TestCase):
# if the functionality of .use_senlin is meant to be used during an # if the functionality of .use_senlin is meant to be used during an
# actual test case, use .get_senlin_discovery_mock and apply to the # actual test case, use .get_senlin_discovery_mock and apply to the
# right location in the mock_uris when calling .register_uris # right location in the mock_uris when calling .register_uris
self.__do_register_uris([ self.__do_register_uris([self.get_senlin_discovery_mock_dict()])
self.get_senlin_discovery_mock_dict()])
def use_cyborg(self): def use_cyborg(self):
# NOTE(s_shogo): This method is only meant to be used in "setUp" # NOTE(s_shogo): This method is only meant to be used in "setUp"
@ -595,8 +750,7 @@ class TestCase(base.TestCase):
# if the functionality of .use_cyborg is meant to be used during an # if the functionality of .use_cyborg is meant to be used during an
# actual test case, use .get_cyborg_discovery_mock and apply to the # actual test case, use .get_cyborg_discovery_mock and apply to the
# right location in the mock_uris when calling .register_uris # right location in the mock_uris when calling .register_uris
self.__do_register_uris([ self.__do_register_uris([self.get_cyborg_discovery_mock_dict()])
self.get_cyborg_discovery_mock_dict()])
def use_manila(self): def use_manila(self):
# NOTE(gouthamr): This method is only meant to be used in "setUp" # NOTE(gouthamr): This method is only meant to be used in "setUp"
@ -604,8 +758,7 @@ class TestCase(base.TestCase):
# if the functionality of .use_manila is meant to be used during an # if the functionality of .use_manila is meant to be used during an
# actual test case, use .get_manila_discovery_mock and apply to the # actual test case, use .get_manila_discovery_mock and apply to the
# right location in the mock_uris when calling .register_uris # right location in the mock_uris when calling .register_uris
self.__do_register_uris([ self.__do_register_uris([self.get_manila_discovery_mock_dict()])
self.get_manila_discovery_mock_dict()])
def register_uris(self, uri_mock_list=None): def register_uris(self, uri_mock_list=None):
"""Mock a list of URIs and responses via requests mock. """Mock a list of URIs and responses via requests mock.
@ -645,10 +798,11 @@ class TestCase(base.TestCase):
def __do_register_uris(self, uri_mock_list=None): def __do_register_uris(self, uri_mock_list=None):
for to_mock in uri_mock_list: for to_mock in uri_mock_list:
kw_params = {k: to_mock.pop(k) kw_params = {
for k in ('request_headers', 'complete_qs', k: to_mock.pop(k)
'_real_http') for k in ('request_headers', 'complete_qs', '_real_http')
if k in to_mock} if k in to_mock
}
method = to_mock.pop('method') method = to_mock.pop('method')
uri = to_mock.pop('uri') uri = to_mock.pop('uri')
@ -656,44 +810,51 @@ class TestCase(base.TestCase):
# case "|" is used so that the split can be a bit easier on # case "|" is used so that the split can be a bit easier on
# maintainers of this code. # maintainers of this code.
key = '{method}|{uri}|{params}'.format( key = '{method}|{uri}|{params}'.format(
method=method, uri=uri, params=kw_params) method=method, uri=uri, params=kw_params
)
validate = to_mock.pop('validate', {}) validate = to_mock.pop('validate', {})
valid_keys = set(['json', 'headers', 'params', 'data']) valid_keys = set(['json', 'headers', 'params', 'data'])
invalid_keys = set(validate.keys()) - valid_keys invalid_keys = set(validate.keys()) - valid_keys
if invalid_keys: if invalid_keys:
raise TypeError( raise TypeError(
"Invalid values passed to validate: {keys}".format( "Invalid values passed to validate: {keys}".format(
keys=invalid_keys)) keys=invalid_keys
headers = structures.CaseInsensitiveDict(to_mock.pop('headers', )
{})) )
headers = structures.CaseInsensitiveDict(
to_mock.pop('headers', {})
)
if 'content-type' not in headers: if 'content-type' not in headers:
headers[u'content-type'] = 'application/json' headers[u'content-type'] = 'application/json'
if 'exc' not in to_mock: if 'exc' not in to_mock:
to_mock['headers'] = headers to_mock['headers'] = headers
self.calls += [ self.calls += [dict(method=method, url=uri, **validate)]
dict(
method=method,
url=uri, **validate)
]
self._uri_registry.setdefault( self._uri_registry.setdefault(
key, {'response_list': [], 'kw_params': kw_params}) key, {'response_list': [], 'kw_params': kw_params}
)
if self._uri_registry[key]['kw_params'] != kw_params: if self._uri_registry[key]['kw_params'] != kw_params:
raise AssertionError( raise AssertionError(
'PROGRAMMING ERROR: key-word-params ' 'PROGRAMMING ERROR: key-word-params '
'should be part of the uri_key and cannot change, ' 'should be part of the uri_key and cannot change, '
'it will affect the matcher in requests_mock. ' 'it will affect the matcher in requests_mock. '
'%(old)r != %(new)r' % '%(old)r != %(new)r'
{'old': self._uri_registry[key]['kw_params'], % {
'new': kw_params}) 'old': self._uri_registry[key]['kw_params'],
'new': kw_params,
}
)
self._uri_registry[key]['response_list'].append(to_mock) self._uri_registry[key]['response_list'].append(to_mock)
for mocked, params in self._uri_registry.items(): for mocked, params in self._uri_registry.items():
mock_method, mock_uri, _ignored = mocked.split('|', 2) mock_method, mock_uri, _ignored = mocked.split('|', 2)
self.adapter.register_uri( self.adapter.register_uri(
mock_method, mock_uri, params['response_list'], mock_method,
**params['kw_params']) mock_uri,
params['response_list'],
**params['kw_params']
)
def assert_no_calls(self): def assert_no_calls(self):
# TODO(mordred) For now, creating the adapter for self.conn is # TODO(mordred) For now, creating the adapter for self.conn is
@ -704,46 +865,65 @@ class TestCase(base.TestCase):
def assert_calls(self, stop_after=None, do_count=True): def assert_calls(self, stop_after=None, do_count=True):
for (x, (call, history)) in enumerate( for (x, (call, history)) in enumerate(
zip(self.calls, self.adapter.request_history)): zip(self.calls, self.adapter.request_history)
):
if stop_after and x > stop_after: if stop_after and x > stop_after:
break break
call_uri_parts = urllib.parse.urlparse(call['url']) call_uri_parts = urllib.parse.urlparse(call['url'])
history_uri_parts = urllib.parse.urlparse(history.url) history_uri_parts = urllib.parse.urlparse(history.url)
self.assertEqual( self.assertEqual(
(call['method'], call_uri_parts.scheme, call_uri_parts.netloc, (
call_uri_parts.path, call_uri_parts.params, call['method'],
urllib.parse.parse_qs(call_uri_parts.query)), call_uri_parts.scheme,
(history.method, history_uri_parts.scheme, call_uri_parts.netloc,
history_uri_parts.netloc, history_uri_parts.path, call_uri_parts.path,
history_uri_parts.params, call_uri_parts.params,
urllib.parse.parse_qs(history_uri_parts.query)), urllib.parse.parse_qs(call_uri_parts.query),
('REST mismatch on call %(index)d. Expected %(call)r. ' ),
'Got %(history)r). ' (
'NOTE: query string order differences wont cause mismatch' % history.method,
{ history_uri_parts.scheme,
'index': x, history_uri_parts.netloc,
'call': '{method} {url}'.format(method=call['method'], history_uri_parts.path,
url=call['url']), history_uri_parts.params,
'history': '{method} {url}'.format( urllib.parse.parse_qs(history_uri_parts.query),
method=history.method, ),
url=history.url)}) (
'REST mismatch on call %(index)d. Expected %(call)r. '
'Got %(history)r). '
'NOTE: query string order differences wont cause mismatch'
% {
'index': x,
'call': '{method} {url}'.format(
method=call['method'], url=call['url']
),
'history': '{method} {url}'.format(
method=history.method, url=history.url
),
}
),
) )
if 'json' in call: if 'json' in call:
self.assertEqual( self.assertEqual(
call['json'], history.json(), call['json'],
'json content mismatch in call {index}'.format(index=x)) history.json(),
'json content mismatch in call {index}'.format(index=x),
)
# headers in a call isn't exhaustive - it's checking to make sure # headers in a call isn't exhaustive - it's checking to make sure
# a specific header or headers are there, not that they are the # a specific header or headers are there, not that they are the
# only headers # only headers
if 'headers' in call: if 'headers' in call:
for key, value in call['headers'].items(): for key, value in call['headers'].items():
self.assertEqual( self.assertEqual(
value, history.headers[key], value,
'header mismatch in call {index}'.format(index=x)) history.headers[key],
'header mismatch in call {index}'.format(index=x),
)
if do_count: if do_count:
self.assertEqual( self.assertEqual(
len(self.calls), len(self.adapter.request_history)) len(self.calls), len(self.adapter.request_history)
)
def assertResourceEqual(self, actual, expected, resource_type): def assertResourceEqual(self, actual, expected, resource_type):
"""Helper for the assertEqual which compares Resource object against """Helper for the assertEqual which compares Resource object against
@ -756,7 +936,7 @@ class TestCase(base.TestCase):
""" """
return self.assertEqual( return self.assertEqual(
resource_type(**expected).to_dict(computed=False), resource_type(**expected).to_dict(computed=False),
actual.to_dict(computed=False) actual.to_dict(computed=False),
) )
def assertResourceListEqual(self, actual, expected, resource_type): def assertResourceListEqual(self, actual, expected, resource_type):
@ -771,12 +951,11 @@ class TestCase(base.TestCase):
""" """
self.assertEqual( self.assertEqual(
[resource_type(**f).to_dict(computed=False) for f in expected], [resource_type(**f).to_dict(computed=False) for f in expected],
[f.to_dict(computed=False) for f in actual] [f.to_dict(computed=False) for f in actual],
) )
class IronicTestCase(TestCase): class IronicTestCase(TestCase):
def setUp(self): def setUp(self):
super(IronicTestCase, self).setUp() super(IronicTestCase, self).setUp()
self.use_ironic() self.use_ironic()

View File

@ -706,7 +706,9 @@ class TestCreateServer(base.TestCase):
] ]
) )
self.cloud.create_server( self.cloud.create_server(
'server-name', dict(id='image-id'), dict(id='flavor-id'), 'server-name',
dict(id='image-id'),
dict(id='flavor-id'),
wait=True, wait=True,
), ),

View File

@ -23,7 +23,6 @@ IDENTIFIER = 'IDENTIFIER'
class TestMetadata(base.TestCase): class TestMetadata(base.TestCase):
def setUp(self): def setUp(self):
super(TestMetadata, self).setUp() super(TestMetadata, self).setUp()
@ -95,8 +94,7 @@ class TestMetadata(base.TestCase):
self.assertEqual(res, result) self.assertEqual(res, result)
url = self.base_path + '/' + res.id + '/metadata' url = self.base_path + '/' + res.id + '/metadata'
self.session.post.assert_called_once_with( self.session.post.assert_called_once_with(
url, url, json={'metadata': {'foo': 'bar'}}
json={'metadata': {'foo': 'bar'}}
) )
def test_replace_metadata(self): def test_replace_metadata(self):
@ -109,8 +107,7 @@ class TestMetadata(base.TestCase):
self.assertEqual(res, result) self.assertEqual(res, result)
url = self.base_path + '/' + res.id + '/metadata' url = self.base_path + '/' + res.id + '/metadata'
self.session.put.assert_called_once_with( self.session.put.assert_called_once_with(
url, url, json={'metadata': {'foo': 'bar'}}
json={'metadata': {'foo': 'bar'}}
) )
def test_delete_all_metadata(self): def test_delete_all_metadata(self):
@ -125,9 +122,7 @@ class TestMetadata(base.TestCase):
# Check passed resource is returned # Check passed resource is returned
self.assertEqual(res, result) self.assertEqual(res, result)
url = self.base_path + '/' + res.id + '/metadata' url = self.base_path + '/' + res.id + '/metadata'
self.session.put.assert_called_once_with( self.session.put.assert_called_once_with(url, json={'metadata': {}})
url,
json={'metadata': {}})
def test_get_metadata_item(self): def test_get_metadata_item(self):
res = self.sot res = self.sot
@ -198,5 +193,5 @@ class TestMetadata(base.TestCase):
self.assertEqual(res, result) self.assertEqual(res, result)
url = self.base_path + '/' + res.id + '/metadata/foo' url = self.base_path + '/' + res.id + '/metadata/foo'
self.session.put.assert_called_once_with( self.session.put.assert_called_once_with(
url, url, json={'meta': {'foo': 'black'}}
json={'meta': {'foo': 'black'}}) )

View File

@ -25,26 +25,13 @@ BASIC_EXAMPLE = {
} }
USAGE_EXAMPLE = { USAGE_EXAMPLE = {
"backup_gigabytes": { "backup_gigabytes": {"in_use": 0, "limit": 1000, "reserved": 0},
"in_use": 0, "backups": {"in_use": 0, "limit": 10, "reserved": 0},
"limit": 1000, "gigabytes___DEFAULT__": {"in_use": 0, "limit": -1, "reserved": 0},
"reserved": 0
},
"backups": {
"in_use": 0,
"limit": 10,
"reserved": 0
},
"gigabytes___DEFAULT__": {
"in_use": 0,
"limit": -1,
"reserved": 0
}
} }
class TestQuotaSet(base.TestCase): class TestQuotaSet(base.TestCase):
def setUp(self): def setUp(self):
super(TestQuotaSet, self).setUp() super(TestQuotaSet, self).setUp()
self.sess = mock.Mock(spec=adapter.Adapter) self.sess = mock.Mock(spec=adapter.Adapter)
@ -64,10 +51,9 @@ class TestQuotaSet(base.TestCase):
self.assertTrue(sot.allow_commit) self.assertTrue(sot.allow_commit)
self.assertDictEqual( self.assertDictEqual(
{"usage": "usage", {"usage": "usage", "limit": "limit", "marker": "marker"},
"limit": "limit", sot._query_mapping._mapping,
"marker": "marker"}, )
sot._query_mapping._mapping)
def test_make_basic(self): def test_make_basic(self):
sot = _qs.QuotaSet(**BASIC_EXAMPLE) sot = _qs.QuotaSet(**BASIC_EXAMPLE)
@ -87,10 +73,8 @@ class TestQuotaSet(base.TestCase):
sot.fetch(self.sess) sot.fetch(self.sess)
self.sess.get.assert_called_with( self.sess.get.assert_called_with(
'/os-quota-sets/proj', '/os-quota-sets/proj', microversion=1, params={}, skip_cache=False
microversion=1, )
params={},
skip_cache=False)
self.assertEqual(BASIC_EXAMPLE['backups'], sot.backups) self.assertEqual(BASIC_EXAMPLE['backups'], sot.backups)
self.assertEqual({}, sot.reservation) self.assertEqual({}, sot.reservation)
@ -112,11 +96,10 @@ class TestQuotaSet(base.TestCase):
'/os-quota-sets/proj', '/os-quota-sets/proj',
microversion=1, microversion=1,
params={'usage': True}, params={'usage': True},
skip_cache=False) skip_cache=False,
)
self.assertEqual( self.assertEqual(USAGE_EXAMPLE['backups']['limit'], sot.backups)
USAGE_EXAMPLE['backups']['limit'],
sot.backups)
def test_update_quota(self): def test_update_quota(self):
# Use QuotaSet as if it was returned by get(usage=True) # Use QuotaSet as if it was returned by get(usage=True)
@ -124,7 +107,8 @@ class TestQuotaSet(base.TestCase):
project_id='proj', project_id='proj',
reservation={'a': 'b'}, reservation={'a': 'b'},
usage={'c': 'd'}, usage={'c': 'd'},
foo='bar') foo='bar',
)
resp = mock.Mock() resp = mock.Mock()
resp.body = {'quota_set': copy.deepcopy(BASIC_EXAMPLE)} resp.body = {'quota_set': copy.deepcopy(BASIC_EXAMPLE)}
@ -133,10 +117,7 @@ class TestQuotaSet(base.TestCase):
resp.headers = {} resp.headers = {}
self.sess.put = mock.Mock(return_value=resp) self.sess.put = mock.Mock(return_value=resp)
sot._update( sot._update(reservation={'b': 'd'}, backups=15, something_else=20)
reservation={'b': 'd'},
backups=15,
something_else=20)
sot.commit(self.sess) sot.commit(self.sess)
@ -144,12 +125,8 @@ class TestQuotaSet(base.TestCase):
'/os-quota-sets/proj', '/os-quota-sets/proj',
microversion=1, microversion=1,
headers={}, headers={},
json={ json={'quota_set': {'backups': 15, 'something_else': 20}},
'quota_set': { )
'backups': 15,
'something_else': 20
}
})
def test_delete_quota(self): def test_delete_quota(self):
# Use QuotaSet as if it was returned by get(usage=True) # Use QuotaSet as if it was returned by get(usage=True)
@ -157,7 +134,8 @@ class TestQuotaSet(base.TestCase):
project_id='proj', project_id='proj',
reservation={'a': 'b'}, reservation={'a': 'b'},
usage={'c': 'd'}, usage={'c': 'd'},
foo='bar') foo='bar',
)
resp = mock.Mock() resp = mock.Mock()
resp.body = None resp.body = None

View File

@ -21,7 +21,6 @@ from openstack.tests.unit.test_resource import FakeResponse
class TestTagMixin(base.TestCase): class TestTagMixin(base.TestCase):
def setUp(self): def setUp(self):
super(TestTagMixin, self).setUp() super(TestTagMixin, self).setUp()
@ -94,10 +93,7 @@ class TestTagMixin(base.TestCase):
# Check the passed resource is returned # Check the passed resource is returned
self.assertEqual(res, result) self.assertEqual(res, result)
url = self.base_path + '/' + res.id + '/tags' url = self.base_path + '/' + res.id + '/tags'
sess.put.assert_called_once_with( sess.put.assert_called_once_with(url, json={'tags': ['blue', 'green']})
url,
json={'tags': ['blue', 'green']}
)
def test_remove_all_tags(self): def test_remove_all_tags(self):
res = self.sot res = self.sot

View File

@ -48,10 +48,7 @@ USER_CONF = {
'force_ipv4': True, 'force_ipv4': True,
}, },
'metrics': { 'metrics': {
'statsd': { 'statsd': {'host': '127.0.0.1', 'port': '1234'},
'host': '127.0.0.1',
'port': '1234'
},
'influxdb': { 'influxdb': {
'host': '127.0.0.1', 'host': '127.0.0.1',
'port': '1234', 'port': '1234',
@ -61,7 +58,7 @@ USER_CONF = {
'database': 'database', 'database': 'database',
'measurement': 'measurement.name', 'measurement': 'measurement.name',
'timeout': 10, 'timeout': 10,
} },
}, },
'clouds': { 'clouds': {
'_test-cloud_': { '_test-cloud_': {
@ -112,30 +109,37 @@ USER_CONF = {
'domain_id': '6789', 'domain_id': '6789',
'project_domain_id': '123456789', 'project_domain_id': '123456789',
}, },
'networks': [{ 'networks': [
'name': 'a-public', {
'routes_externally': True, 'name': 'a-public',
'nat_source': True, 'routes_externally': True,
}, { 'nat_source': True,
'name': 'another-public', },
'routes_externally': True, {
'default_interface': True, 'name': 'another-public',
}, { 'routes_externally': True,
'name': 'a-private', 'default_interface': True,
'routes_externally': False, },
}, { {
'name': 'another-private', 'name': 'a-private',
'routes_externally': False, 'routes_externally': False,
'nat_destination': True, },
}, { {
'name': 'split-default', 'name': 'another-private',
'routes_externally': True, 'routes_externally': False,
'routes_ipv4_externally': False, 'nat_destination': True,
}, { },
'name': 'split-no-default', {
'routes_ipv6_externally': False, 'name': 'split-default',
'routes_ipv4_externally': True, 'routes_externally': True,
}], 'routes_ipv4_externally': False,
},
{
'name': 'split-no-default',
'routes_ipv6_externally': False,
'routes_ipv4_externally': True,
},
],
'region_name': 'test-region', 'region_name': 'test-region',
}, },
'_test_cloud_regions': { '_test_cloud_regions': {
@ -150,13 +154,13 @@ USER_CONF = {
'name': 'region1', 'name': 'region1',
'values': { 'values': {
'external_network': 'region1-network', 'external_network': 'region1-network',
} },
}, },
{ {
'name': 'region2', 'name': 'region2',
'values': { 'values': {
'external_network': 'my-network', 'external_network': 'my-network',
} },
}, },
{ {
'name': 'region-no-value', 'name': 'region-no-value',
@ -198,13 +202,13 @@ USER_CONF = {
'statsd': { 'statsd': {
'host': '127.0.0.1', 'host': '127.0.0.1',
'port': 4321, 'port': 4321,
'prefix': 'statsd.override.prefix' 'prefix': 'statsd.override.prefix',
}, },
'influxdb': { 'influxdb': {
'username': 'override-username', 'username': 'override-username',
'password': 'override-password', 'password': 'override-password',
'database': 'override-database', 'database': 'override-database',
} },
}, },
}, },
}, },

View File

@ -40,7 +40,6 @@ fake_services_dict = {
class TestCloudRegion(base.TestCase): class TestCloudRegion(base.TestCase):
def test_arbitrary_attributes(self): def test_arbitrary_attributes(self):
cc = cloud_region.CloudRegion("test1", "region-al", fake_config_dict) cc = cloud_region.CloudRegion("test1", "region-al", fake_config_dict)
self.assertEqual("test1", cc.name) self.assertEqual("test1", cc.name)
@ -89,12 +88,10 @@ class TestCloudRegion(base.TestCase):
self.assertIsNone(cc._get_config('nothing', None)) self.assertIsNone(cc._get_config('nothing', None))
# This is what is happening behind the scenes in get_default_interface. # This is what is happening behind the scenes in get_default_interface.
self.assertEqual( self.assertEqual(
fake_services_dict['interface'], fake_services_dict['interface'], cc._get_config('interface', None)
cc._get_config('interface', None)) )
# The same call as above, but from one step up the stack # The same call as above, but from one step up the stack
self.assertEqual( self.assertEqual(fake_services_dict['interface'], cc.get_interface())
fake_services_dict['interface'],
cc.get_interface())
# Which finally is what is called to populate the below # Which finally is what is called to populate the below
self.assertEqual('public', self.cloud.default_interface) self.assertEqual('public', self.cloud.default_interface)
@ -150,16 +147,21 @@ class TestCloudRegion(base.TestCase):
def test_ipv6(self): def test_ipv6(self):
cc = cloud_region.CloudRegion( cc = cloud_region.CloudRegion(
"test1", "region-al", fake_config_dict, force_ipv4=True) "test1", "region-al", fake_config_dict, force_ipv4=True
)
self.assertTrue(cc.force_ipv4) self.assertTrue(cc.force_ipv4)
def test_getters(self): def test_getters(self):
cc = cloud_region.CloudRegion("test1", "region-al", fake_services_dict) cc = cloud_region.CloudRegion("test1", "region-al", fake_services_dict)
self.assertEqual(['compute', 'identity', 'image', 'volume'], self.assertEqual(
sorted(cc.get_services())) ['compute', 'identity', 'image', 'volume'],
self.assertEqual({'password': 'hunter2', 'username': 'AzureDiamond'}, sorted(cc.get_services()),
cc.get_auth_args()) )
self.assertEqual(
{'password': 'hunter2', 'username': 'AzureDiamond'},
cc.get_auth_args(),
)
self.assertEqual('public', cc.get_interface()) self.assertEqual('public', cc.get_interface())
self.assertEqual('public', cc.get_interface('compute')) self.assertEqual('public', cc.get_interface('compute'))
self.assertEqual('admin', cc.get_interface('identity')) self.assertEqual('admin', cc.get_interface('identity'))
@ -170,8 +172,9 @@ class TestCloudRegion(base.TestCase):
self.assertEqual('compute', cc.get_service_type('compute')) self.assertEqual('compute', cc.get_service_type('compute'))
self.assertEqual('1', cc.get_api_version('volume')) self.assertEqual('1', cc.get_api_version('volume'))
self.assertEqual('block-storage', cc.get_service_type('volume')) self.assertEqual('block-storage', cc.get_service_type('volume'))
self.assertEqual('http://compute.example.com', self.assertEqual(
cc.get_endpoint('compute')) 'http://compute.example.com', cc.get_endpoint('compute')
)
self.assertIsNone(cc.get_endpoint('image')) self.assertIsNone(cc.get_endpoint('image'))
self.assertIsNone(cc.get_service_name('compute')) self.assertIsNone(cc.get_service_name('compute'))
self.assertEqual('locks', cc.get_service_name('identity')) self.assertEqual('locks', cc.get_service_name('identity'))
@ -184,38 +187,45 @@ class TestCloudRegion(base.TestCase):
# We're skipping loader here, so we have to expand relevant # We're skipping loader here, so we have to expand relevant
# parts from the rackspace profile. The thing we're testing # parts from the rackspace profile. The thing we're testing
# is that the project_id logic works. # is that the project_id logic works.
cc = cloud_region.CloudRegion("test1", "DFW", { cc = cloud_region.CloudRegion(
'profile': 'rackspace', "test1",
'region_name': 'DFW', "DFW",
'auth': {'project_id': '123456'}, {
'block_storage_endpoint_override': 'https://example.com/v2/', 'profile': 'rackspace',
}) 'region_name': 'DFW',
'auth': {'project_id': '123456'},
'block_storage_endpoint_override': 'https://example.com/v2/',
},
)
self.assertEqual( self.assertEqual(
'https://example.com/v2/123456', 'https://example.com/v2/123456', cc.get_endpoint('block-storage')
cc.get_endpoint('block-storage')
) )
def test_rackspace_workaround_only_rax(self): def test_rackspace_workaround_only_rax(self):
cc = cloud_region.CloudRegion("test1", "DFW", { cc = cloud_region.CloudRegion(
'region_name': 'DFW', "test1",
'auth': {'project_id': '123456'}, "DFW",
'block_storage_endpoint_override': 'https://example.com/v2/', {
}) 'region_name': 'DFW',
'auth': {'project_id': '123456'},
'block_storage_endpoint_override': 'https://example.com/v2/',
},
)
self.assertEqual( self.assertEqual(
'https://example.com/v2/', 'https://example.com/v2/', cc.get_endpoint('block-storage')
cc.get_endpoint('block-storage')
) )
def test_get_region_name(self): def test_get_region_name(self):
def assert_region_name(default, compute): def assert_region_name(default, compute):
self.assertEqual(default, cc.region_name) self.assertEqual(default, cc.region_name)
self.assertEqual(default, cc.get_region_name()) self.assertEqual(default, cc.get_region_name())
self.assertEqual(default, cc.get_region_name(service_type=None)) self.assertEqual(default, cc.get_region_name(service_type=None))
self.assertEqual( self.assertEqual(
compute, cc.get_region_name(service_type='compute')) compute, cc.get_region_name(service_type='compute')
)
self.assertEqual( self.assertEqual(
default, cc.get_region_name(service_type='placement')) default, cc.get_region_name(service_type='placement')
)
# No region_name kwarg, no regions specified in services dict # No region_name kwarg, no regions specified in services dict
# (including the default). # (including the default).
@ -224,14 +234,17 @@ class TestCloudRegion(base.TestCase):
# Only region_name kwarg; it's returned for everything # Only region_name kwarg; it's returned for everything
cc = cloud_region.CloudRegion( cc = cloud_region.CloudRegion(
region_name='foo', config=fake_services_dict) region_name='foo', config=fake_services_dict
)
assert_region_name('foo', 'foo') assert_region_name('foo', 'foo')
# No region_name kwarg; values (including default) show through from # No region_name kwarg; values (including default) show through from
# config dict # config dict
services_dict = dict( services_dict = dict(
fake_services_dict, fake_services_dict,
region_name='the-default', compute_region_name='compute-region') region_name='the-default',
compute_region_name='compute-region',
)
cc = cloud_region.CloudRegion(config=services_dict) cc = cloud_region.CloudRegion(config=services_dict)
assert_region_name('the-default', 'compute-region') assert_region_name('the-default', 'compute-region')
@ -239,9 +252,12 @@ class TestCloudRegion(base.TestCase):
# compatibility), but service-specific region_name takes precedence. # compatibility), but service-specific region_name takes precedence.
services_dict = dict( services_dict = dict(
fake_services_dict, fake_services_dict,
region_name='dict', compute_region_name='compute-region') region_name='dict',
compute_region_name='compute-region',
)
cc = cloud_region.CloudRegion( cc = cloud_region.CloudRegion(
region_name='kwarg', config=services_dict) region_name='kwarg', config=services_dict
)
assert_region_name('kwarg', 'compute-region') assert_region_name('kwarg', 'compute-region')
def test_aliases(self): def test_aliases(self):
@ -265,9 +281,7 @@ class TestCloudRegion(base.TestCase):
config_dict = defaults.get_defaults() config_dict = defaults.get_defaults()
config_dict.update(fake_services_dict) config_dict.update(fake_services_dict)
cc = cloud_region.CloudRegion("test1", "region-al", config_dict) cc = cloud_region.CloudRegion("test1", "region-al", config_dict)
self.assertRaises( self.assertRaises(exceptions.ConfigException, cc.get_session)
exceptions.ConfigException,
cc.get_session)
@mock.patch.object(ksa_session, 'Session') @mock.patch.object(ksa_session, 'Session')
def test_get_session(self, mock_session): def test_get_session(self, mock_session):
@ -277,15 +291,21 @@ class TestCloudRegion(base.TestCase):
fake_session.additional_user_agent = [] fake_session.additional_user_agent = []
mock_session.return_value = fake_session mock_session.return_value = fake_session
cc = cloud_region.CloudRegion( cc = cloud_region.CloudRegion(
"test1", "region-al", config_dict, auth_plugin=mock.Mock()) "test1", "region-al", config_dict, auth_plugin=mock.Mock()
)
cc.get_session() cc.get_session()
mock_session.assert_called_with( mock_session.assert_called_with(
auth=mock.ANY, auth=mock.ANY,
verify=True, cert=None, timeout=None, collect_timing=None, verify=True,
discovery_cache=None) cert=None,
timeout=None,
collect_timing=None,
discovery_cache=None,
)
self.assertEqual( self.assertEqual(
fake_session.additional_user_agent, fake_session.additional_user_agent,
[('openstacksdk', openstack_version.__version__)]) [('openstacksdk', openstack_version.__version__)],
)
@mock.patch.object(ksa_session, 'Session') @mock.patch.object(ksa_session, 'Session')
def test_get_session_with_app_name(self, mock_session): def test_get_session_with_app_name(self, mock_session):
@ -297,18 +317,28 @@ class TestCloudRegion(base.TestCase):
fake_session.app_version = None fake_session.app_version = None
mock_session.return_value = fake_session mock_session.return_value = fake_session
cc = cloud_region.CloudRegion( cc = cloud_region.CloudRegion(
"test1", "region-al", config_dict, auth_plugin=mock.Mock(), "test1",
app_name="test_app", app_version="test_version") "region-al",
config_dict,
auth_plugin=mock.Mock(),
app_name="test_app",
app_version="test_version",
)
cc.get_session() cc.get_session()
mock_session.assert_called_with( mock_session.assert_called_with(
auth=mock.ANY, auth=mock.ANY,
verify=True, cert=None, timeout=None, collect_timing=None, verify=True,
discovery_cache=None) cert=None,
timeout=None,
collect_timing=None,
discovery_cache=None,
)
self.assertEqual(fake_session.app_name, "test_app") self.assertEqual(fake_session.app_name, "test_app")
self.assertEqual(fake_session.app_version, "test_version") self.assertEqual(fake_session.app_version, "test_version")
self.assertEqual( self.assertEqual(
fake_session.additional_user_agent, fake_session.additional_user_agent,
[('openstacksdk', openstack_version.__version__)]) [('openstacksdk', openstack_version.__version__)],
)
@mock.patch.object(ksa_session, 'Session') @mock.patch.object(ksa_session, 'Session')
def test_get_session_with_timeout(self, mock_session): def test_get_session_with_timeout(self, mock_session):
@ -319,15 +349,21 @@ class TestCloudRegion(base.TestCase):
config_dict.update(fake_services_dict) config_dict.update(fake_services_dict)
config_dict['api_timeout'] = 9 config_dict['api_timeout'] = 9
cc = cloud_region.CloudRegion( cc = cloud_region.CloudRegion(
"test1", "region-al", config_dict, auth_plugin=mock.Mock()) "test1", "region-al", config_dict, auth_plugin=mock.Mock()
)
cc.get_session() cc.get_session()
mock_session.assert_called_with( mock_session.assert_called_with(
auth=mock.ANY, auth=mock.ANY,
verify=True, cert=None, timeout=9, verify=True,
collect_timing=None, discovery_cache=None) cert=None,
timeout=9,
collect_timing=None,
discovery_cache=None,
)
self.assertEqual( self.assertEqual(
fake_session.additional_user_agent, fake_session.additional_user_agent,
[('openstacksdk', openstack_version.__version__)]) [('openstacksdk', openstack_version.__version__)],
)
@mock.patch.object(ksa_session, 'Session') @mock.patch.object(ksa_session, 'Session')
def test_get_session_with_timing(self, mock_session): def test_get_session_with_timing(self, mock_session):
@ -338,35 +374,45 @@ class TestCloudRegion(base.TestCase):
config_dict.update(fake_services_dict) config_dict.update(fake_services_dict)
config_dict['timing'] = True config_dict['timing'] = True
cc = cloud_region.CloudRegion( cc = cloud_region.CloudRegion(
"test1", "region-al", config_dict, auth_plugin=mock.Mock()) "test1", "region-al", config_dict, auth_plugin=mock.Mock()
)
cc.get_session() cc.get_session()
mock_session.assert_called_with( mock_session.assert_called_with(
auth=mock.ANY, auth=mock.ANY,
verify=True, cert=None, timeout=None, verify=True,
collect_timing=True, discovery_cache=None) cert=None,
timeout=None,
collect_timing=True,
discovery_cache=None,
)
self.assertEqual( self.assertEqual(
fake_session.additional_user_agent, fake_session.additional_user_agent,
[('openstacksdk', openstack_version.__version__)]) [('openstacksdk', openstack_version.__version__)],
)
@mock.patch.object(ksa_session, 'Session') @mock.patch.object(ksa_session, 'Session')
def test_override_session_endpoint_override(self, mock_session): def test_override_session_endpoint_override(self, mock_session):
config_dict = defaults.get_defaults() config_dict = defaults.get_defaults()
config_dict.update(fake_services_dict) config_dict.update(fake_services_dict)
cc = cloud_region.CloudRegion( cc = cloud_region.CloudRegion(
"test1", "region-al", config_dict, auth_plugin=mock.Mock()) "test1", "region-al", config_dict, auth_plugin=mock.Mock()
)
self.assertEqual( self.assertEqual(
cc.get_session_endpoint('compute'), cc.get_session_endpoint('compute'),
fake_services_dict['compute_endpoint_override']) fake_services_dict['compute_endpoint_override'],
)
@mock.patch.object(ksa_session, 'Session') @mock.patch.object(ksa_session, 'Session')
def test_override_session_endpoint(self, mock_session): def test_override_session_endpoint(self, mock_session):
config_dict = defaults.get_defaults() config_dict = defaults.get_defaults()
config_dict.update(fake_services_dict) config_dict.update(fake_services_dict)
cc = cloud_region.CloudRegion( cc = cloud_region.CloudRegion(
"test1", "region-al", config_dict, auth_plugin=mock.Mock()) "test1", "region-al", config_dict, auth_plugin=mock.Mock()
)
self.assertEqual( self.assertEqual(
cc.get_session_endpoint('telemetry'), cc.get_session_endpoint('telemetry'),
fake_services_dict['telemetry_endpoint']) fake_services_dict['telemetry_endpoint'],
)
@mock.patch.object(cloud_region.CloudRegion, 'get_session') @mock.patch.object(cloud_region.CloudRegion, 'get_session')
def test_session_endpoint(self, mock_get_session): def test_session_endpoint(self, mock_get_session):
@ -375,20 +421,23 @@ class TestCloudRegion(base.TestCase):
config_dict = defaults.get_defaults() config_dict = defaults.get_defaults()
config_dict.update(fake_services_dict) config_dict.update(fake_services_dict)
cc = cloud_region.CloudRegion( cc = cloud_region.CloudRegion(
"test1", "region-al", config_dict, auth_plugin=mock.Mock()) "test1", "region-al", config_dict, auth_plugin=mock.Mock()
)
cc.get_session_endpoint('orchestration') cc.get_session_endpoint('orchestration')
mock_session.get_endpoint.assert_called_with( mock_session.get_endpoint.assert_called_with(
interface='public', interface='public',
service_name=None, service_name=None,
region_name='region-al', region_name='region-al',
service_type='orchestration') service_type='orchestration',
)
@mock.patch.object(cloud_region.CloudRegion, 'get_session') @mock.patch.object(cloud_region.CloudRegion, 'get_session')
def test_session_endpoint_not_found(self, mock_get_session): def test_session_endpoint_not_found(self, mock_get_session):
exc_to_raise = ksa_exceptions.catalog.EndpointNotFound exc_to_raise = ksa_exceptions.catalog.EndpointNotFound
mock_get_session.return_value.get_endpoint.side_effect = exc_to_raise mock_get_session.return_value.get_endpoint.side_effect = exc_to_raise
cc = cloud_region.CloudRegion( cc = cloud_region.CloudRegion(
"test1", "region-al", {}, auth_plugin=mock.Mock()) "test1", "region-al", {}, auth_plugin=mock.Mock()
)
self.assertIsNone(cc.get_session_endpoint('notfound')) self.assertIsNone(cc.get_session_endpoint('notfound'))
def test_get_endpoint_from_catalog(self): def test_get_endpoint_from_catalog(self):
@ -396,14 +445,20 @@ class TestCloudRegion(base.TestCase):
self.cloud.config.config['dns_endpoint_override'] = dns_override self.cloud.config.config['dns_endpoint_override'] = dns_override
self.assertEqual( self.assertEqual(
'https://compute.example.com/v2.1/', 'https://compute.example.com/v2.1/',
self.cloud.config.get_endpoint_from_catalog('compute')) self.cloud.config.get_endpoint_from_catalog('compute'),
)
self.assertEqual( self.assertEqual(
'https://internal.compute.example.com/v2.1/', 'https://internal.compute.example.com/v2.1/',
self.cloud.config.get_endpoint_from_catalog( self.cloud.config.get_endpoint_from_catalog(
'compute', interface='internal')) 'compute', interface='internal'
),
)
self.assertIsNone( self.assertIsNone(
self.cloud.config.get_endpoint_from_catalog( self.cloud.config.get_endpoint_from_catalog(
'compute', region_name='unknown-region')) 'compute', region_name='unknown-region'
)
)
self.assertEqual( self.assertEqual(
'https://dns.example.com', 'https://dns.example.com',
self.cloud.config.get_endpoint_from_catalog('dns')) self.cloud.config.get_endpoint_from_catalog('dns'),
)

File diff suppressed because it is too large Load Diff

View File

@ -21,52 +21,64 @@ from openstack.tests.unit.config import base
class TestEnviron(base.TestCase): class TestEnviron(base.TestCase):
def setUp(self): def setUp(self):
super(TestEnviron, self).setUp() super(TestEnviron, self).setUp()
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable('OS_AUTH_URL', 'https://example.com')) fixtures.EnvironmentVariable('OS_AUTH_URL', 'https://example.com')
)
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable('OS_USERNAME', 'testuser')) fixtures.EnvironmentVariable('OS_USERNAME', 'testuser')
)
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable('OS_PASSWORD', 'testpass')) fixtures.EnvironmentVariable('OS_PASSWORD', 'testpass')
)
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable('OS_PROJECT_NAME', 'testproject')) fixtures.EnvironmentVariable('OS_PROJECT_NAME', 'testproject')
)
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable('NOVA_PROJECT_ID', 'testnova')) fixtures.EnvironmentVariable('NOVA_PROJECT_ID', 'testnova')
)
def test_get_one(self): def test_get_one(self):
c = config.OpenStackConfig(config_files=[self.cloud_yaml], c = config.OpenStackConfig(
vendor_files=[self.vendor_yaml]) config_files=[self.cloud_yaml], vendor_files=[self.vendor_yaml]
)
self.assertIsInstance(c.get_one(), cloud_region.CloudRegion) self.assertIsInstance(c.get_one(), cloud_region.CloudRegion)
def test_no_fallthrough(self): def test_no_fallthrough(self):
c = config.OpenStackConfig(config_files=[self.cloud_yaml], c = config.OpenStackConfig(
vendor_files=[self.vendor_yaml]) config_files=[self.cloud_yaml], vendor_files=[self.vendor_yaml]
self.assertRaises( )
exceptions.ConfigException, c.get_one, 'openstack') self.assertRaises(exceptions.ConfigException, c.get_one, 'openstack')
def test_envvar_name_override(self): def test_envvar_name_override(self):
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable('OS_CLOUD_NAME', 'override')) fixtures.EnvironmentVariable('OS_CLOUD_NAME', 'override')
c = config.OpenStackConfig(config_files=[self.cloud_yaml], )
vendor_files=[self.vendor_yaml]) c = config.OpenStackConfig(
config_files=[self.cloud_yaml], vendor_files=[self.vendor_yaml]
)
cc = c.get_one('override') cc = c.get_one('override')
self._assert_cloud_details(cc) self._assert_cloud_details(cc)
def test_envvar_prefer_ipv6_override(self): def test_envvar_prefer_ipv6_override(self):
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable('OS_PREFER_IPV6', 'false')) fixtures.EnvironmentVariable('OS_PREFER_IPV6', 'false')
c = config.OpenStackConfig(config_files=[self.cloud_yaml], )
vendor_files=[self.vendor_yaml], c = config.OpenStackConfig(
secure_files=[self.secure_yaml]) config_files=[self.cloud_yaml],
vendor_files=[self.vendor_yaml],
secure_files=[self.secure_yaml],
)
cc = c.get_one('_test-cloud_') cc = c.get_one('_test-cloud_')
self.assertFalse(cc.prefer_ipv6) self.assertFalse(cc.prefer_ipv6)
def test_environ_exists(self): def test_environ_exists(self):
c = config.OpenStackConfig(config_files=[self.cloud_yaml], c = config.OpenStackConfig(
vendor_files=[self.vendor_yaml], config_files=[self.cloud_yaml],
secure_files=[self.secure_yaml]) vendor_files=[self.vendor_yaml],
secure_files=[self.secure_yaml],
)
cc = c.get_one('envvars') cc = c.get_one('envvars')
self._assert_cloud_details(cc) self._assert_cloud_details(cc)
self.assertNotIn('auth_url', cc.config) self.assertNotIn('auth_url', cc.config)
@ -79,10 +91,12 @@ class TestEnviron(base.TestCase):
self._assert_cloud_details(cc) self._assert_cloud_details(cc)
def test_environ_prefix(self): def test_environ_prefix(self):
c = config.OpenStackConfig(config_files=[self.cloud_yaml], c = config.OpenStackConfig(
vendor_files=[self.vendor_yaml], config_files=[self.cloud_yaml],
envvar_prefix='NOVA_', vendor_files=[self.vendor_yaml],
secure_files=[self.secure_yaml]) envvar_prefix='NOVA_',
secure_files=[self.secure_yaml],
)
cc = c.get_one('envvars') cc = c.get_one('envvars')
self._assert_cloud_details(cc) self._assert_cloud_details(cc)
self.assertNotIn('auth_url', cc.config) self.assertNotIn('auth_url', cc.config)
@ -95,9 +109,11 @@ class TestEnviron(base.TestCase):
self._assert_cloud_details(cc) self._assert_cloud_details(cc)
def test_get_one_with_config_files(self): def test_get_one_with_config_files(self):
c = config.OpenStackConfig(config_files=[self.cloud_yaml], c = config.OpenStackConfig(
vendor_files=[self.vendor_yaml], config_files=[self.cloud_yaml],
secure_files=[self.secure_yaml]) vendor_files=[self.vendor_yaml],
secure_files=[self.secure_yaml],
)
self.assertIsInstance(c.cloud_config, dict) self.assertIsInstance(c.cloud_config, dict)
self.assertIn('cache', c.cloud_config) self.assertIn('cache', c.cloud_config)
self.assertIsInstance(c.cloud_config['cache'], dict) self.assertIsInstance(c.cloud_config['cache'], dict)
@ -111,40 +127,40 @@ class TestEnviron(base.TestCase):
def test_config_file_override(self): def test_config_file_override(self):
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable( fixtures.EnvironmentVariable(
'OS_CLIENT_CONFIG_FILE', self.cloud_yaml)) 'OS_CLIENT_CONFIG_FILE', self.cloud_yaml
c = config.OpenStackConfig(config_files=[], )
vendor_files=[self.vendor_yaml]) )
c = config.OpenStackConfig(
config_files=[], vendor_files=[self.vendor_yaml]
)
cc = c.get_one('_test-cloud_') cc = c.get_one('_test-cloud_')
self._assert_cloud_details(cc) self._assert_cloud_details(cc)
class TestEnvvars(base.TestCase): class TestEnvvars(base.TestCase):
def test_no_envvars(self): def test_no_envvars(self):
self.useFixture( self.useFixture(fixtures.EnvironmentVariable('NOVA_USERNAME', 'nova'))
fixtures.EnvironmentVariable('NOVA_USERNAME', 'nova')) c = config.OpenStackConfig(
c = config.OpenStackConfig(config_files=[self.cloud_yaml], config_files=[self.cloud_yaml], vendor_files=[self.vendor_yaml]
vendor_files=[self.vendor_yaml]) )
self.assertRaises( self.assertRaises(exceptions.ConfigException, c.get_one, 'envvars')
exceptions.ConfigException, c.get_one, 'envvars')
def test_test_envvars(self): def test_test_envvars(self):
self.useFixture(fixtures.EnvironmentVariable('NOVA_USERNAME', 'nova'))
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable('NOVA_USERNAME', 'nova')) fixtures.EnvironmentVariable('OS_STDERR_CAPTURE', 'True')
self.useFixture( )
fixtures.EnvironmentVariable('OS_STDERR_CAPTURE', 'True')) c = config.OpenStackConfig(
c = config.OpenStackConfig(config_files=[self.cloud_yaml], config_files=[self.cloud_yaml], vendor_files=[self.vendor_yaml]
vendor_files=[self.vendor_yaml]) )
self.assertRaises( self.assertRaises(exceptions.ConfigException, c.get_one, 'envvars')
exceptions.ConfigException, c.get_one, 'envvars')
def test_incomplete_envvars(self): def test_incomplete_envvars(self):
self.useFixture( self.useFixture(fixtures.EnvironmentVariable('NOVA_USERNAME', 'nova'))
fixtures.EnvironmentVariable('NOVA_USERNAME', 'nova')) self.useFixture(fixtures.EnvironmentVariable('OS_USERNAME', 'user'))
self.useFixture( config.OpenStackConfig(
fixtures.EnvironmentVariable('OS_USERNAME', 'user')) config_files=[self.cloud_yaml], vendor_files=[self.vendor_yaml]
config.OpenStackConfig(config_files=[self.cloud_yaml], )
vendor_files=[self.vendor_yaml])
# This is broken due to an issue that's fixed in a subsequent patch # This is broken due to an issue that's fixed in a subsequent patch
# commenting it out in this patch to keep the patch size reasonable # commenting it out in this patch to keep the patch size reasonable
# self.assertRaises( # self.assertRaises(
@ -152,33 +168,38 @@ class TestEnvvars(base.TestCase):
# c.get_one, 'envvars') # c.get_one, 'envvars')
def test_have_envvars(self): def test_have_envvars(self):
self.useFixture(fixtures.EnvironmentVariable('NOVA_USERNAME', 'nova'))
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable('NOVA_USERNAME', 'nova')) fixtures.EnvironmentVariable('OS_AUTH_URL', 'http://example.com')
)
self.useFixture(fixtures.EnvironmentVariable('OS_USERNAME', 'user'))
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable('OS_AUTH_URL', 'http://example.com')) fixtures.EnvironmentVariable('OS_PASSWORD', 'password')
)
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable('OS_USERNAME', 'user')) fixtures.EnvironmentVariable('OS_PROJECT_NAME', 'project')
self.useFixture( )
fixtures.EnvironmentVariable('OS_PASSWORD', 'password')) c = config.OpenStackConfig(
self.useFixture( config_files=[self.cloud_yaml], vendor_files=[self.vendor_yaml]
fixtures.EnvironmentVariable('OS_PROJECT_NAME', 'project')) )
c = config.OpenStackConfig(config_files=[self.cloud_yaml],
vendor_files=[self.vendor_yaml])
cc = c.get_one('envvars') cc = c.get_one('envvars')
self.assertEqual(cc.config['auth']['username'], 'user') self.assertEqual(cc.config['auth']['username'], 'user')
def test_old_envvars(self): def test_old_envvars(self):
self.useFixture(fixtures.EnvironmentVariable('NOVA_USERNAME', 'nova'))
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable('NOVA_USERNAME', 'nova')) fixtures.EnvironmentVariable('NOVA_AUTH_URL', 'http://example.com')
)
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable( fixtures.EnvironmentVariable('NOVA_PASSWORD', 'password')
'NOVA_AUTH_URL', 'http://example.com')) )
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable('NOVA_PASSWORD', 'password')) fixtures.EnvironmentVariable('NOVA_PROJECT_NAME', 'project')
self.useFixture( )
fixtures.EnvironmentVariable('NOVA_PROJECT_NAME', 'project')) c = config.OpenStackConfig(
c = config.OpenStackConfig(config_files=[self.cloud_yaml], config_files=[self.cloud_yaml],
vendor_files=[self.vendor_yaml], vendor_files=[self.vendor_yaml],
envvar_prefix='NOVA_') envvar_prefix='NOVA_',
)
cc = c.get_one('envvars') cc = c.get_one('envvars')
self.assertEqual(cc.config['auth']['username'], 'nova') self.assertEqual(cc.config['auth']['username'], 'nova')

View File

@ -23,13 +23,15 @@ from openstack.tests.unit import base
class TestFromConf(base.TestCase): class TestFromConf(base.TestCase):
def _get_conn(self, **from_conf_kwargs): def _get_conn(self, **from_conf_kwargs):
oslocfg = self._load_ks_cfg_opts() oslocfg = self._load_ks_cfg_opts()
# Throw name in here to prove **kwargs is working # Throw name in here to prove **kwargs is working
config = cloud_region.from_conf( config = cloud_region.from_conf(
oslocfg, session=self.cloud.session, name='from_conf.example.com', oslocfg,
**from_conf_kwargs) session=self.cloud.session,
name='from_conf.example.com',
**from_conf_kwargs
)
self.assertEqual('from_conf.example.com', config.name) self.assertEqual('from_conf.example.com', config.name)
return connection.Connection(config=config, strict_proxies=True) return connection.Connection(config=config, strict_proxies=True)
@ -41,33 +43,48 @@ class TestFromConf(base.TestCase):
discovery = { discovery = {
"versions": { "versions": {
"values": [ "values": [
{"status": "stable", {
"updated": "2019-06-01T00:00:00Z", "status": "stable",
"media-types": [{ "updated": "2019-06-01T00:00:00Z",
"base": "application/json", "media-types": [
"type": "application/vnd.openstack.heat-v2+json"}], {
"id": "v2.0", "base": "application/json",
"links": [{ "type": "application/vnd.openstack.heat-v2+json", # noqa: E501
"href": "https://example.org:8888/heat/v2", }
"rel": "self"}] ],
}] "id": "v2.0",
"links": [
{
"href": "https://example.org:8888/heat/v2",
"rel": "self",
}
],
}
]
} }
} }
self.register_uris([ self.register_uris(
dict(method='GET', [
uri='https://example.org:8888/heat/v2', dict(
json=discovery), method='GET',
dict(method='GET', uri='https://example.org:8888/heat/v2',
uri='https://example.org:8888/heat/v2/foo', json=discovery,
json={'foo': {}}), ),
]) dict(
method='GET',
uri='https://example.org:8888/heat/v2/foo',
json={'foo': {}},
),
]
)
adap = conn.orchestration adap = conn.orchestration
self.assertEqual('SpecialRegion', adap.region_name) self.assertEqual('SpecialRegion', adap.region_name)
self.assertEqual('orchestration', adap.service_type) self.assertEqual('orchestration', adap.service_type)
self.assertEqual('internal', adap.interface) self.assertEqual('internal', adap.interface)
self.assertEqual('https://example.org:8888/heat/v2', self.assertEqual(
adap.endpoint_override) 'https://example.org:8888/heat/v2', adap.endpoint_override
)
adap.get('/foo') adap.get('/foo')
self.assert_calls() self.assert_calls()
@ -80,13 +97,18 @@ class TestFromConf(base.TestCase):
server_name = self.getUniqueString('name') server_name = self.getUniqueString('name')
fake_server = fakes.make_fake_server(server_id, server_name) fake_server = fakes.make_fake_server(server_id, server_name)
self.register_uris([ self.register_uris(
self.get_nova_discovery_mock_dict(), [
dict(method='GET', self.get_nova_discovery_mock_dict(),
uri=self.get_mock_url( dict(
'compute', 'public', append=['servers', 'detail']), method='GET',
json={'servers': [fake_server]}), uri=self.get_mock_url(
]) 'compute', 'public', append=['servers', 'detail']
),
json={'servers': [fake_server]},
),
]
)
# Nova has empty adapter config, so these default # Nova has empty adapter config, so these default
adap = conn.compute adap = conn.compute
@ -108,20 +130,27 @@ class TestFromConf(base.TestCase):
server_name = self.getUniqueString('name') server_name = self.getUniqueString('name')
fake_server = fakes.make_fake_server(server_id, server_name) fake_server = fakes.make_fake_server(server_id, server_name)
self.register_uris([ self.register_uris(
dict(method='GET', [
uri='https://compute.example.com/v2.1/', dict(
exc=requests.exceptions.ConnectionError), method='GET',
self.get_nova_discovery_mock_dict(), uri='https://compute.example.com/v2.1/',
dict(method='GET', exc=requests.exceptions.ConnectionError,
uri=self.get_mock_url( ),
'compute', 'public', append=['servers', 'detail']), self.get_nova_discovery_mock_dict(),
json={'servers': [fake_server]}), dict(
]) method='GET',
uri=self.get_mock_url(
'compute', 'public', append=['servers', 'detail']
),
json={'servers': [fake_server]},
),
]
)
self.assertRaises( self.assertRaises(
exceptions.ServiceDiscoveryException, exceptions.ServiceDiscoveryException, getattr, conn, 'compute'
getattr, conn, 'compute') )
# Nova has empty adapter config, so these default # Nova has empty adapter config, so these default
adap = conn.compute adap = conn.compute
@ -141,31 +170,41 @@ class TestFromConf(base.TestCase):
discovery = { discovery = {
"versions": { "versions": {
"values": [ "values": [
{"status": "stable", {
"id": "v1", "status": "stable",
"links": [{ "id": "v1",
"href": "https://example.org:5050/v1", "links": [
"rel": "self"}] {
}] "href": "https://example.org:5050/v1",
"rel": "self",
}
],
}
]
} }
} }
status = { status = {'finished': True, 'error': None}
'finished': True, self.register_uris(
'error': None [
} dict(
self.register_uris([ method='GET',
dict(method='GET', uri='https://example.org:5050',
uri='https://example.org:5050', json=discovery,
json=discovery), ),
# strict-proxies means we're going to fetch the discovery # strict-proxies means we're going to fetch the discovery
# doc from the versioned endpoint to verify it works. # doc from the versioned endpoint to verify it works.
dict(method='GET', dict(
uri='https://example.org:5050/v1', method='GET',
json=discovery), uri='https://example.org:5050/v1',
dict(method='GET', json=discovery,
uri='https://example.org:5050/v1/introspection/abcd', ),
json=status), dict(
]) method='GET',
uri='https://example.org:5050/v1/introspection/abcd',
json=status,
),
]
)
adap = conn.baremetal_introspection adap = conn.baremetal_introspection
self.assertEqual('baremetal-introspection', adap.service_type) self.assertEqual('baremetal-introspection', adap.service_type)
@ -180,38 +219,53 @@ class TestFromConf(base.TestCase):
discovery = { discovery = {
"versions": { "versions": {
"values": [ "values": [
{"status": "stable", {
"id": "v1", "status": "stable",
"links": [{ "id": "v1",
"href": "https://example.org:5050/v1", "links": [
"rel": "self"}] {
}] "href": "https://example.org:5050/v1",
"rel": "self",
}
],
}
]
} }
} }
status = { status = {'finished': True, 'error': None}
'finished': True, self.register_uris(
'error': None [
} dict(
self.register_uris([ method='GET',
dict(method='GET', uri='https://example.org:5050',
uri='https://example.org:5050', exc=requests.exceptions.ConnectTimeout,
exc=requests.exceptions.ConnectTimeout), ),
dict(method='GET', dict(
uri='https://example.org:5050', method='GET',
json=discovery), uri='https://example.org:5050',
# strict-proxies means we're going to fetch the discovery json=discovery,
# doc from the versioned endpoint to verify it works. ),
dict(method='GET', # strict-proxies means we're going to fetch the discovery
uri='https://example.org:5050/v1', # doc from the versioned endpoint to verify it works.
json=discovery), dict(
dict(method='GET', method='GET',
uri='https://example.org:5050/v1/introspection/abcd', uri='https://example.org:5050/v1',
json=status), json=discovery,
]) ),
dict(
method='GET',
uri='https://example.org:5050/v1/introspection/abcd',
json=status,
),
]
)
self.assertRaises( self.assertRaises(
exceptions.ServiceDiscoveryException, exceptions.ServiceDiscoveryException,
getattr, conn, 'baremetal_introspection') getattr,
conn,
'baremetal_introspection',
)
adap = conn.baremetal_introspection adap = conn.baremetal_introspection
self.assertEqual('baremetal-introspection', adap.service_type) self.assertEqual('baremetal-introspection', adap.service_type)
@ -220,16 +274,21 @@ class TestFromConf(base.TestCase):
self.assertTrue(adap.get_introspection('abcd').is_finished) self.assertTrue(adap.get_introspection('abcd').is_finished)
def assert_service_disabled(self, service_type, expected_reason, def assert_service_disabled(
**from_conf_kwargs): self, service_type, expected_reason, **from_conf_kwargs
):
conn = self._get_conn(**from_conf_kwargs) conn = self._get_conn(**from_conf_kwargs)
# The _ServiceDisabledProxyShim loads up okay... # The _ServiceDisabledProxyShim loads up okay...
adap = getattr(conn, service_type) adap = getattr(conn, service_type)
# ...but freaks out if you try to use it. # ...but freaks out if you try to use it.
ex = self.assertRaises( ex = self.assertRaises(
exceptions.ServiceDisabledException, getattr, adap, 'get') exceptions.ServiceDisabledException, getattr, adap, 'get'
self.assertIn("Service '%s' is disabled because its configuration " )
"could not be loaded." % service_type, ex.message) self.assertIn(
"Service '%s' is disabled because its configuration "
"could not be loaded." % service_type,
ex.message,
)
self.assertIn(expected_reason, ex.message) self.assertIn(expected_reason, ex.message)
def test_no_such_conf_section(self): def test_no_such_conf_section(self):
@ -238,15 +297,18 @@ class TestFromConf(base.TestCase):
self.assert_service_disabled( self.assert_service_disabled(
'orchestration', 'orchestration',
"No section for project 'heat' (service type 'orchestration') was " "No section for project 'heat' (service type 'orchestration') was "
"present in the config.") "present in the config.",
)
def test_no_such_conf_section_ignore_service_type(self): def test_no_such_conf_section_ignore_service_type(self):
"""Ignore absent conf section if service type not requested.""" """Ignore absent conf section if service type not requested."""
del self.oslo_config_dict['heat'] del self.oslo_config_dict['heat']
self.assert_service_disabled( self.assert_service_disabled(
'orchestration', "Not in the list of requested service_types.", 'orchestration',
"Not in the list of requested service_types.",
# 'orchestration' absent from this list # 'orchestration' absent from this list
service_types=['compute']) service_types=['compute'],
)
def test_no_adapter_opts(self): def test_no_adapter_opts(self):
"""Conf section present, but opts for service type not registered.""" """Conf section present, but opts for service type not registered."""
@ -254,15 +316,18 @@ class TestFromConf(base.TestCase):
self.assert_service_disabled( self.assert_service_disabled(
'orchestration', 'orchestration',
"Encountered an exception attempting to process config for " "Encountered an exception attempting to process config for "
"project 'heat' (service type 'orchestration'): no such option") "project 'heat' (service type 'orchestration'): no such option",
)
def test_no_adapter_opts_ignore_service_type(self): def test_no_adapter_opts_ignore_service_type(self):
"""Ignore unregistered conf section if service type not requested.""" """Ignore unregistered conf section if service type not requested."""
self.oslo_config_dict['heat'] = None self.oslo_config_dict['heat'] = None
self.assert_service_disabled( self.assert_service_disabled(
'orchestration', "Not in the list of requested service_types.", 'orchestration',
"Not in the list of requested service_types.",
# 'orchestration' absent from this list # 'orchestration' absent from this list
service_types=['compute']) service_types=['compute'],
)
def test_invalid_adapter_opts(self): def test_invalid_adapter_opts(self):
"""Adapter opts are bogus, in exception-raising ways.""" """Adapter opts are bogus, in exception-raising ways."""
@ -274,24 +339,31 @@ class TestFromConf(base.TestCase):
'orchestration', 'orchestration',
"Encountered an exception attempting to process config for " "Encountered an exception attempting to process config for "
"project 'heat' (service type 'orchestration'): interface and " "project 'heat' (service type 'orchestration'): interface and "
"valid_interfaces are mutually exclusive.") "valid_interfaces are mutually exclusive.",
)
def test_no_session(self): def test_no_session(self):
# TODO(efried): Currently calling without a Session is not implemented. # TODO(efried): Currently calling without a Session is not implemented.
self.assertRaises(exceptions.ConfigException, self.assertRaises(
cloud_region.from_conf, self._load_ks_cfg_opts()) exceptions.ConfigException,
cloud_region.from_conf,
self._load_ks_cfg_opts(),
)
def test_no_endpoint(self): def test_no_endpoint(self):
"""Conf contains adapter opts, but service type not in catalog.""" """Conf contains adapter opts, but service type not in catalog."""
self.os_fixture.v3_token.remove_service('monitoring') self.os_fixture.v3_token.remove_service('monitoring')
conn = self._get_conn() conn = self._get_conn()
# Monasca is not in the service catalog # Monasca is not in the service catalog
self.assertRaises(ks_exc.catalog.EndpointNotFound, self.assertRaises(
getattr, conn, 'monitoring') ks_exc.catalog.EndpointNotFound, getattr, conn, 'monitoring'
)
def test_no_endpoint_ignore_service_type(self): def test_no_endpoint_ignore_service_type(self):
"""Bogus service type disabled if not in requested service_types.""" """Bogus service type disabled if not in requested service_types."""
self.assert_service_disabled( self.assert_service_disabled(
'monitoring', "Not in the list of requested service_types.", 'monitoring',
"Not in the list of requested service_types.",
# 'monitoring' absent from this list # 'monitoring' absent from this list
service_types={'compute', 'orchestration', 'bogus'}) service_types={'compute', 'orchestration', 'bogus'},
)

View File

@ -30,7 +30,8 @@ class TestFromSession(base.TestCase):
def test_from_session(self): def test_from_session(self):
config = cloud_region.from_session( config = cloud_region.from_session(
self.cloud.session, region_name=self.test_region) self.cloud.session, region_name=self.test_region
)
self.assertEqual(config.name, 'identity.example.com') self.assertEqual(config.name, 'identity.example.com')
if not self.test_region: if not self.test_region:
self.assertIsNone(config.region_name) self.assertIsNone(config.region_name)
@ -40,13 +41,18 @@ class TestFromSession(base.TestCase):
server_id = str(uuid.uuid4()) server_id = str(uuid.uuid4())
server_name = self.getUniqueString('name') server_name = self.getUniqueString('name')
fake_server = fakes.make_fake_server(server_id, server_name) fake_server = fakes.make_fake_server(server_id, server_name)
self.register_uris([ self.register_uris(
self.get_nova_discovery_mock_dict(), [
dict(method='GET', self.get_nova_discovery_mock_dict(),
uri=self.get_mock_url( dict(
'compute', 'public', append=['servers', 'detail']), method='GET',
json={'servers': [fake_server]}), uri=self.get_mock_url(
]) 'compute', 'public', append=['servers', 'detail']
),
json={'servers': [fake_server]},
),
]
)
conn = connection.Connection(config=config) conn = connection.Connection(config=config)
s = next(conn.compute.servers()) s = next(conn.compute.servers())

View File

@ -19,17 +19,16 @@ from openstack.tests.unit.config import base
class TestInit(base.TestCase): class TestInit(base.TestCase):
def test_get_cloud_region_without_arg_parser(self): def test_get_cloud_region_without_arg_parser(self):
cloud_region = openstack.config.get_cloud_region( cloud_region = openstack.config.get_cloud_region(
options=None, validate=False) options=None, validate=False
)
self.assertIsInstance( self.assertIsInstance(
cloud_region, cloud_region, openstack.config.cloud_region.CloudRegion
openstack.config.cloud_region.CloudRegion
) )
def test_get_cloud_region_with_arg_parser(self): def test_get_cloud_region_with_arg_parser(self):
cloud_region = openstack.config.get_cloud_region( cloud_region = openstack.config.get_cloud_region(
options=argparse.ArgumentParser(), options=argparse.ArgumentParser(), validate=False
validate=False) )
self.assertIsInstance( self.assertIsInstance(
cloud_region, cloud_region, openstack.config.cloud_region.CloudRegion
openstack.config.cloud_region.CloudRegion
) )

View File

@ -24,7 +24,6 @@ from openstack.tests.unit.config import base
class TestConfig(base.TestCase): class TestConfig(base.TestCase):
def json_diagnostics(self, exc_info): def json_diagnostics(self, exc_info):
self.addDetail('filename', content.text_content(self.filename)) self.addDetail('filename', content.text_content(self.filename))
for error in sorted(self.validator.iter_errors(self.json_data)): for error in sorted(self.validator.iter_errors(self.json_data)):
@ -32,8 +31,8 @@ class TestConfig(base.TestCase):
def test_defaults_valid_json(self): def test_defaults_valid_json(self):
_schema_path = os.path.join( _schema_path = os.path.join(
os.path.dirname(os.path.realpath(defaults.__file__)), os.path.dirname(os.path.realpath(defaults.__file__)), 'schema.json'
'schema.json') )
with open(_schema_path, 'r') as f: with open(_schema_path, 'r') as f:
schema = json.load(f) schema = json.load(f)
self.validator = jsonschema.Draft4Validator(schema) self.validator = jsonschema.Draft4Validator(schema)
@ -41,7 +40,8 @@ class TestConfig(base.TestCase):
self.filename = os.path.join( self.filename = os.path.join(
os.path.dirname(os.path.realpath(defaults.__file__)), os.path.dirname(os.path.realpath(defaults.__file__)),
'defaults.json') 'defaults.json',
)
with open(self.filename, 'r') as f: with open(self.filename, 'r') as f:
self.json_data = json.load(f) self.json_data = json.load(f)
@ -50,7 +50,8 @@ class TestConfig(base.TestCase):
def test_vendors_valid_json(self): def test_vendors_valid_json(self):
_schema_path = os.path.join( _schema_path = os.path.join(
os.path.dirname(os.path.realpath(defaults.__file__)), os.path.dirname(os.path.realpath(defaults.__file__)),
'vendor-schema.json') 'vendor-schema.json',
)
with open(_schema_path, 'r') as f: with open(_schema_path, 'r') as f:
schema = json.load(f) schema = json.load(f)
self.validator = jsonschema.Draft4Validator(schema) self.validator = jsonschema.Draft4Validator(schema)
@ -58,8 +59,8 @@ class TestConfig(base.TestCase):
self.addOnException(self.json_diagnostics) self.addOnException(self.json_diagnostics)
_vendors_path = os.path.join( _vendors_path = os.path.join(
os.path.dirname(os.path.realpath(defaults.__file__)), os.path.dirname(os.path.realpath(defaults.__file__)), 'vendors'
'vendors') )
for self.filename in glob.glob(os.path.join(_vendors_path, '*.json')): for self.filename in glob.glob(os.path.join(_vendors_path, '*.json')):
with open(self.filename, 'r') as f: with open(self.filename, 'r') as f:
self.json_data = json.load(f) self.json_data = json.load(f)

View File

@ -21,14 +21,17 @@ from openstack import exceptions
from openstack.tests.unit.config import base from openstack.tests.unit.config import base
FILES = { FILES = {
'yaml': textwrap.dedent(''' 'yaml': textwrap.dedent(
'''
foo: bar foo: bar
baz: baz:
- 1 - 1
- 2 - 2
- 3 - 3
'''), '''
'json': textwrap.dedent(''' ),
'json': textwrap.dedent(
'''
{ {
"foo": "bar", "foo": "bar",
"baz": [ "baz": [
@ -37,18 +40,20 @@ FILES = {
3 3
] ]
} }
'''), '''
'txt': textwrap.dedent(''' ),
'txt': textwrap.dedent(
'''
foo foo
bar baz bar baz
test test
one two one two
'''), '''
),
} }
class TestLoader(base.TestCase): class TestLoader(base.TestCase):
def test_base_load_yaml_json_file(self): def test_base_load_yaml_json_file(self):
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
tested_files = [] tested_files = []
@ -59,7 +64,8 @@ class TestLoader(base.TestCase):
tested_files.append(fn) tested_files.append(fn)
path, result = loader.OpenStackConfig()._load_yaml_json_file( path, result = loader.OpenStackConfig()._load_yaml_json_file(
tested_files) tested_files
)
# NOTE(hberaud): Prefer to test path rather than file because # NOTE(hberaud): Prefer to test path rather than file because
# our FILES var is a dict so results are appened # our FILES var is a dict so results are appened
# without keeping the initial order (python 3.5) # without keeping the initial order (python 3.5)
@ -77,7 +83,8 @@ class TestLoader(base.TestCase):
tested_files.append(fn) tested_files.append(fn)
path, result = loader.OpenStackConfig()._load_yaml_json_file( path, result = loader.OpenStackConfig()._load_yaml_json_file(
tested_files) tested_files
)
# NOTE(hberaud): Prefer to test path rather than file because # NOTE(hberaud): Prefer to test path rather than file because
# our FILES var is a dict so results are appened # our FILES var is a dict so results are appened
# without keeping the initial order (python 3.5) # without keeping the initial order (python 3.5)
@ -92,7 +99,8 @@ class TestLoader(base.TestCase):
tested_files.append(fn) tested_files.append(fn)
path, result = loader.OpenStackConfig()._load_yaml_json_file( path, result = loader.OpenStackConfig()._load_yaml_json_file(
tested_files) tested_files
)
self.assertEqual(fn, path) self.assertEqual(fn, path)
def test__load_yaml_json_file_without_perm(self): def test__load_yaml_json_file_without_perm(self):
@ -105,7 +113,8 @@ class TestLoader(base.TestCase):
tested_files.append(fn) tested_files.append(fn)
path, result = loader.OpenStackConfig()._load_yaml_json_file( path, result = loader.OpenStackConfig()._load_yaml_json_file(
tested_files) tested_files
)
self.assertEqual(None, path) self.assertEqual(None, path)
def test__load_yaml_json_file_nonexisting(self): def test__load_yaml_json_file_nonexisting(self):
@ -114,28 +123,56 @@ class TestLoader(base.TestCase):
tested_files.append(fn) tested_files.append(fn)
path, result = loader.OpenStackConfig()._load_yaml_json_file( path, result = loader.OpenStackConfig()._load_yaml_json_file(
tested_files) tested_files
)
self.assertEqual(None, path) self.assertEqual(None, path)
class TestFixArgv(base.TestCase): class TestFixArgv(base.TestCase):
def test_no_changes(self): def test_no_changes(self):
argv = ['-a', '-b', '--long-arg', '--multi-value', 'key1=value1', argv = [
'--multi-value', 'key2=value2'] '-a',
'-b',
'--long-arg',
'--multi-value',
'key1=value1',
'--multi-value',
'key2=value2',
]
expected = argv[:] expected = argv[:]
loader._fix_argv(argv) loader._fix_argv(argv)
self.assertEqual(expected, argv) self.assertEqual(expected, argv)
def test_replace(self): def test_replace(self):
argv = ['-a', '-b', '--long-arg', '--multi_value', 'key1=value1', argv = [
'--multi_value', 'key2=value2'] '-a',
expected = ['-a', '-b', '--long-arg', '--multi-value', 'key1=value1', '-b',
'--multi-value', 'key2=value2'] '--long-arg',
'--multi_value',
'key1=value1',
'--multi_value',
'key2=value2',
]
expected = [
'-a',
'-b',
'--long-arg',
'--multi-value',
'key1=value1',
'--multi-value',
'key2=value2',
]
loader._fix_argv(argv) loader._fix_argv(argv)
self.assertEqual(expected, argv) self.assertEqual(expected, argv)
def test_mix(self): def test_mix(self):
argv = ['-a', '-b', '--long-arg', '--multi_value', 'key1=value1', argv = [
'--multi-value', 'key2=value2'] '-a',
self.assertRaises(exceptions.ConfigException, '-b',
loader._fix_argv, argv) '--long-arg',
'--multi_value',
'key1=value1',
'--multi-value',
'key2=value2',
]
self.assertRaises(exceptions.ConfigException, loader._fix_argv, argv)

View File

@ -70,9 +70,13 @@ clouds:
password: {password} password: {password}
project_name: {project} project_name: {project}
cacert: {cacert} cacert: {cacert}
""".format(auth_url=CONFIG_AUTH_URL, username=CONFIG_USERNAME, """.format(
password=CONFIG_PASSWORD, project=CONFIG_PROJECT, auth_url=CONFIG_AUTH_URL,
cacert=CONFIG_CACERT) username=CONFIG_USERNAME,
password=CONFIG_PASSWORD,
project=CONFIG_PROJECT,
cacert=CONFIG_CACERT,
)
VENDOR_CONFIG = """ VENDOR_CONFIG = """
{{ {{
@ -84,7 +88,9 @@ VENDOR_CONFIG = """
"vendor_hook": "openstack.tests.unit.test_connection:vendor_hook" "vendor_hook": "openstack.tests.unit.test_connection:vendor_hook"
}} }}
}} }}
""".format(auth_url=CONFIG_AUTH_URL) """.format(
auth_url=CONFIG_AUTH_URL
)
PUBLIC_CLOUDS_YAML = """ PUBLIC_CLOUDS_YAML = """
public-clouds: public-clouds:
@ -92,11 +98,12 @@ public-clouds:
auth: auth:
auth_url: {auth_url} auth_url: {auth_url}
vendor_hook: openstack.tests.unit.test_connection:vendor_hook vendor_hook: openstack.tests.unit.test_connection:vendor_hook
""".format(auth_url=CONFIG_AUTH_URL) """.format(
auth_url=CONFIG_AUTH_URL
)
class _TestConnectionBase(base.TestCase): class _TestConnectionBase(base.TestCase):
def setUp(self): def setUp(self):
super(_TestConnectionBase, self).setUp() super(_TestConnectionBase, self).setUp()
# Create a temporary directory where our test config will live # Create a temporary directory where our test config will live
@ -107,8 +114,9 @@ class _TestConnectionBase(base.TestCase):
with open(config_path, "w") as conf: with open(config_path, "w") as conf:
conf.write(CLOUD_CONFIG) conf.write(CLOUD_CONFIG)
self.useFixture(fixtures.EnvironmentVariable( self.useFixture(
"OS_CLIENT_CONFIG_FILE", config_path)) fixtures.EnvironmentVariable("OS_CLIENT_CONFIG_FILE", config_path)
)
self.use_keystone_v2() self.use_keystone_v2()
@ -152,104 +160,127 @@ class TestConnection(_TestConnectionBase):
# conn.workflow.__class__.__module__) # conn.workflow.__class__.__module__)
def test_create_unknown_proxy(self): def test_create_unknown_proxy(self):
self.register_uris([ self.register_uris(
self.get_placement_discovery_mock_dict(), [
]) self.get_placement_discovery_mock_dict(),
]
)
def closure(): def closure():
return self.cloud.placement return self.cloud.placement
self.assertThat( self.assertThat(closure, matchers.Warnings(matchers.HasLength(0)))
closure,
matchers.Warnings(matchers.HasLength(0)))
self.assertIsInstance( self.assertIsInstance(self.cloud.placement, proxy.Proxy)
self.cloud.placement,
proxy.Proxy)
self.assert_calls() self.assert_calls()
def test_create_connection_version_param_default(self): def test_create_connection_version_param_default(self):
c1 = connection.Connection(cloud='sample-cloud') c1 = connection.Connection(cloud='sample-cloud')
conn = connection.Connection(session=c1.session) conn = connection.Connection(session=c1.session)
self.assertEqual('openstack.identity.v3._proxy', self.assertEqual(
conn.identity.__class__.__module__) 'openstack.identity.v3._proxy', conn.identity.__class__.__module__
)
def test_create_connection_version_param_string(self): def test_create_connection_version_param_string(self):
c1 = connection.Connection(cloud='sample-cloud') c1 = connection.Connection(cloud='sample-cloud')
conn = connection.Connection( conn = connection.Connection(
session=c1.session, identity_api_version='2') session=c1.session, identity_api_version='2'
self.assertEqual('openstack.identity.v2._proxy', )
conn.identity.__class__.__module__) self.assertEqual(
'openstack.identity.v2._proxy', conn.identity.__class__.__module__
)
def test_create_connection_version_param_int(self): def test_create_connection_version_param_int(self):
c1 = connection.Connection(cloud='sample-cloud') c1 = connection.Connection(cloud='sample-cloud')
conn = connection.Connection( conn = connection.Connection(
session=c1.session, identity_api_version=3) session=c1.session, identity_api_version=3
self.assertEqual('openstack.identity.v3._proxy', )
conn.identity.__class__.__module__) self.assertEqual(
'openstack.identity.v3._proxy', conn.identity.__class__.__module__
)
def test_create_connection_version_param_bogus(self): def test_create_connection_version_param_bogus(self):
c1 = connection.Connection(cloud='sample-cloud') c1 = connection.Connection(cloud='sample-cloud')
conn = connection.Connection( conn = connection.Connection(
session=c1.session, identity_api_version='red') session=c1.session, identity_api_version='red'
)
# TODO(mordred) This is obviously silly behavior # TODO(mordred) This is obviously silly behavior
self.assertEqual('openstack.identity.v3._proxy', self.assertEqual(
conn.identity.__class__.__module__) 'openstack.identity.v3._proxy', conn.identity.__class__.__module__
)
def test_from_config_given_config(self): def test_from_config_given_config(self):
cloud_region = (openstack.config.OpenStackConfig(). cloud_region = openstack.config.OpenStackConfig().get_one(
get_one("sample-cloud")) "sample-cloud"
)
sot = connection.from_config(config=cloud_region) sot = connection.from_config(config=cloud_region)
self.assertEqual(CONFIG_USERNAME, self.assertEqual(
sot.config.config['auth']['username']) CONFIG_USERNAME, sot.config.config['auth']['username']
self.assertEqual(CONFIG_PASSWORD, )
sot.config.config['auth']['password']) self.assertEqual(
self.assertEqual(CONFIG_AUTH_URL, CONFIG_PASSWORD, sot.config.config['auth']['password']
sot.config.config['auth']['auth_url']) )
self.assertEqual(CONFIG_PROJECT, self.assertEqual(
sot.config.config['auth']['project_name']) CONFIG_AUTH_URL, sot.config.config['auth']['auth_url']
)
self.assertEqual(
CONFIG_PROJECT, sot.config.config['auth']['project_name']
)
def test_from_config_given_cloud(self): def test_from_config_given_cloud(self):
sot = connection.from_config(cloud="sample-cloud") sot = connection.from_config(cloud="sample-cloud")
self.assertEqual(CONFIG_USERNAME, self.assertEqual(
sot.config.config['auth']['username']) CONFIG_USERNAME, sot.config.config['auth']['username']
self.assertEqual(CONFIG_PASSWORD, )
sot.config.config['auth']['password']) self.assertEqual(
self.assertEqual(CONFIG_AUTH_URL, CONFIG_PASSWORD, sot.config.config['auth']['password']
sot.config.config['auth']['auth_url']) )
self.assertEqual(CONFIG_PROJECT, self.assertEqual(
sot.config.config['auth']['project_name']) CONFIG_AUTH_URL, sot.config.config['auth']['auth_url']
)
self.assertEqual(
CONFIG_PROJECT, sot.config.config['auth']['project_name']
)
def test_from_config_given_cloud_config(self): def test_from_config_given_cloud_config(self):
cloud_region = (openstack.config.OpenStackConfig(). cloud_region = openstack.config.OpenStackConfig().get_one(
get_one("sample-cloud")) "sample-cloud"
)
sot = connection.from_config(cloud_config=cloud_region) sot = connection.from_config(cloud_config=cloud_region)
self.assertEqual(CONFIG_USERNAME, self.assertEqual(
sot.config.config['auth']['username']) CONFIG_USERNAME, sot.config.config['auth']['username']
self.assertEqual(CONFIG_PASSWORD, )
sot.config.config['auth']['password']) self.assertEqual(
self.assertEqual(CONFIG_AUTH_URL, CONFIG_PASSWORD, sot.config.config['auth']['password']
sot.config.config['auth']['auth_url']) )
self.assertEqual(CONFIG_PROJECT, self.assertEqual(
sot.config.config['auth']['project_name']) CONFIG_AUTH_URL, sot.config.config['auth']['auth_url']
)
self.assertEqual(
CONFIG_PROJECT, sot.config.config['auth']['project_name']
)
def test_from_config_given_cloud_name(self): def test_from_config_given_cloud_name(self):
sot = connection.from_config(cloud_name="sample-cloud") sot = connection.from_config(cloud_name="sample-cloud")
self.assertEqual(CONFIG_USERNAME, self.assertEqual(
sot.config.config['auth']['username']) CONFIG_USERNAME, sot.config.config['auth']['username']
self.assertEqual(CONFIG_PASSWORD, )
sot.config.config['auth']['password']) self.assertEqual(
self.assertEqual(CONFIG_AUTH_URL, CONFIG_PASSWORD, sot.config.config['auth']['password']
sot.config.config['auth']['auth_url']) )
self.assertEqual(CONFIG_PROJECT, self.assertEqual(
sot.config.config['auth']['project_name']) CONFIG_AUTH_URL, sot.config.config['auth']['auth_url']
)
self.assertEqual(
CONFIG_PROJECT, sot.config.config['auth']['project_name']
)
def test_from_config_verify(self): def test_from_config_verify(self):
sot = connection.from_config(cloud="insecure-cloud") sot = connection.from_config(cloud="insecure-cloud")
@ -268,25 +299,32 @@ class TestOsloConfig(_TestConnectionBase):
def test_from_conf(self): def test_from_conf(self):
c1 = connection.Connection(cloud='sample-cloud') c1 = connection.Connection(cloud='sample-cloud')
conn = connection.Connection( conn = connection.Connection(
session=c1.session, oslo_conf=self._load_ks_cfg_opts()) session=c1.session, oslo_conf=self._load_ks_cfg_opts()
)
# There was no config for keystone # There was no config for keystone
self.assertIsInstance( self.assertIsInstance(
conn.identity, service_description._ServiceDisabledProxyShim) conn.identity, service_description._ServiceDisabledProxyShim
)
# But nova was in there # But nova was in there
self.assertEqual('openstack.compute.v2._proxy', self.assertEqual(
conn.compute.__class__.__module__) 'openstack.compute.v2._proxy', conn.compute.__class__.__module__
)
def test_from_conf_filter_service_types(self): def test_from_conf_filter_service_types(self):
c1 = connection.Connection(cloud='sample-cloud') c1 = connection.Connection(cloud='sample-cloud')
conn = connection.Connection( conn = connection.Connection(
session=c1.session, oslo_conf=self._load_ks_cfg_opts(), session=c1.session,
service_types={'orchestration', 'i-am-ignored'}) oslo_conf=self._load_ks_cfg_opts(),
service_types={'orchestration', 'i-am-ignored'},
)
# There was no config for keystone # There was no config for keystone
self.assertIsInstance( self.assertIsInstance(
conn.identity, service_description._ServiceDisabledProxyShim) conn.identity, service_description._ServiceDisabledProxyShim
)
# Nova was in there, but disabled because not requested # Nova was in there, but disabled because not requested
self.assertIsInstance( self.assertIsInstance(
conn.compute, service_description._ServiceDisabledProxyShim) conn.compute, service_description._ServiceDisabledProxyShim
)
class TestNetworkConnection(base.TestCase): class TestNetworkConnection(base.TestCase):
@ -298,15 +336,18 @@ class TestNetworkConnection(base.TestCase):
svc.add_endpoint( svc.add_endpoint(
interface='public', interface='public',
url='https://network.example.com/v2.0', url='https://network.example.com/v2.0',
region='RegionOne') region='RegionOne',
)
self.use_keystone_v3() self.use_keystone_v3()
self.assertEqual( self.assertEqual(
'openstack.network.v2._proxy', 'openstack.network.v2._proxy',
self.cloud.network.__class__.__module__) self.cloud.network.__class__.__module__,
)
self.assert_calls() self.assert_calls()
self.assertEqual( self.assertEqual(
"https://network.example.com/v2.0", "https://network.example.com/v2.0",
self.cloud.network.get_endpoint()) self.cloud.network.get_endpoint(),
)
class TestNetworkConnectionSuffix(base.TestCase): class TestNetworkConnectionSuffix(base.TestCase):
@ -316,15 +357,16 @@ class TestNetworkConnectionSuffix(base.TestCase):
def test_network_proxy(self): def test_network_proxy(self):
self.assertEqual( self.assertEqual(
'openstack.network.v2._proxy', 'openstack.network.v2._proxy',
self.cloud.network.__class__.__module__) self.cloud.network.__class__.__module__,
)
self.assert_calls() self.assert_calls()
self.assertEqual( self.assertEqual(
"https://network.example.com/v2.0", "https://network.example.com/v2.0",
self.cloud.network.get_endpoint()) self.cloud.network.get_endpoint(),
)
class TestAuthorize(base.TestCase): class TestAuthorize(base.TestCase):
def test_authorize_works(self): def test_authorize_works(self):
res = self.cloud.authorize() res = self.cloud.authorize()
self.assertEqual('KeystoneToken-1', res) self.assertEqual('KeystoneToken-1', res)
@ -332,12 +374,12 @@ class TestAuthorize(base.TestCase):
def test_authorize_failure(self): def test_authorize_failure(self):
self.use_broken_keystone() self.use_broken_keystone()
self.assertRaises(openstack.exceptions.SDKException, self.assertRaises(
self.cloud.authorize) openstack.exceptions.SDKException, self.cloud.authorize
)
class TestNewService(base.TestCase): class TestNewService(base.TestCase):
def test_add_service_v1(self): def test_add_service_v1(self):
svc = self.os_fixture.v3_token.add_service('fake') svc = self.os_fixture.v3_token.add_service('fake')
svc.add_endpoint( svc.add_endpoint(
@ -355,21 +397,30 @@ class TestNewService(base.TestCase):
# Ensure no discovery calls made # Ensure no discovery calls made
self.assertEqual(0, len(self.adapter.request_history)) self.assertEqual(0, len(self.adapter.request_history))
self.register_uris([ self.register_uris(
dict(method='GET', [
uri='https://fake.example.com', dict(
status_code=404), method='GET',
dict(method='GET', uri='https://fake.example.com',
uri='https://fake.example.com/v1/', status_code=404,
status_code=404), ),
dict(method='GET', dict(
uri=self.get_mock_url('fake'), method='GET',
status_code=404), uri='https://fake.example.com/v1/',
]) status_code=404,
),
dict(
method='GET',
uri=self.get_mock_url('fake'),
status_code=404,
),
]
)
self.assertEqual( self.assertEqual(
'openstack.tests.unit.fake.v1._proxy', 'openstack.tests.unit.fake.v1._proxy',
conn.fake.__class__.__module__) conn.fake.__class__.__module__,
)
self.assertTrue(conn.fake.dummy()) self.assertTrue(conn.fake.dummy())
def test_add_service_v2(self): def test_add_service_v2(self):
@ -382,17 +433,25 @@ class TestNewService(base.TestCase):
self.use_keystone_v3() self.use_keystone_v3()
conn = self.cloud conn = self.cloud
self.register_uris([ self.register_uris(
dict(method='GET', [
uri='https://fake.example.com', dict(
status_code=404), method='GET',
dict(method='GET', uri='https://fake.example.com',
uri='https://fake.example.com/v2/', status_code=404,
status_code=404), ),
dict(method='GET', dict(
uri=self.get_mock_url('fake'), method='GET',
status_code=404), uri='https://fake.example.com/v2/',
]) status_code=404,
),
dict(
method='GET',
uri=self.get_mock_url('fake'),
status_code=404,
),
]
)
service = fake_service.FakeService('fake') service = fake_service.FakeService('fake')
@ -400,7 +459,8 @@ class TestNewService(base.TestCase):
self.assertEqual( self.assertEqual(
'openstack.tests.unit.fake.v2._proxy', 'openstack.tests.unit.fake.v2._proxy',
conn.fake.__class__.__module__) conn.fake.__class__.__module__,
)
self.assertFalse(conn.fake.dummy()) self.assertFalse(conn.fake.dummy())
def test_replace_system_service(self): def test_replace_system_service(self):
@ -416,17 +476,25 @@ class TestNewService(base.TestCase):
# delete native dns service # delete native dns service
delattr(conn, 'dns') delattr(conn, 'dns')
self.register_uris([ self.register_uris(
dict(method='GET', [
uri='https://fake.example.com', dict(
status_code=404), method='GET',
dict(method='GET', uri='https://fake.example.com',
uri='https://fake.example.com/v2/', status_code=404,
status_code=404), ),
dict(method='GET', dict(
uri=self.get_mock_url('fake'), method='GET',
status_code=404), uri='https://fake.example.com/v2/',
]) status_code=404,
),
dict(
method='GET',
uri=self.get_mock_url('fake'),
status_code=404,
),
]
)
# add fake service with alias 'DNS' # add fake service with alias 'DNS'
service = fake_service.FakeService('fake', aliases=['dns']) service = fake_service.FakeService('fake', aliases=['dns'])
@ -441,7 +509,6 @@ def vendor_hook(conn):
class TestVendorProfile(base.TestCase): class TestVendorProfile(base.TestCase):
def setUp(self): def setUp(self):
super(TestVendorProfile, self).setUp() super(TestVendorProfile, self).setUp()
# Create a temporary directory where our test config will live # Create a temporary directory where our test config will live
@ -456,12 +523,14 @@ class TestVendorProfile(base.TestCase):
with open(public_clouds, "w") as conf: with open(public_clouds, "w") as conf:
conf.write(PUBLIC_CLOUDS_YAML) conf.write(PUBLIC_CLOUDS_YAML)
self.useFixture(fixtures.EnvironmentVariable( self.useFixture(
"OS_CLIENT_CONFIG_FILE", config_path)) fixtures.EnvironmentVariable("OS_CLIENT_CONFIG_FILE", config_path)
)
self.use_keystone_v2() self.use_keystone_v2()
self.config = openstack.config.loader.OpenStackConfig( self.config = openstack.config.loader.OpenStackConfig(
vendor_files=[public_clouds]) vendor_files=[public_clouds]
)
def test_conn_from_profile(self): def test_conn_from_profile(self):
@ -483,7 +552,7 @@ class TestVendorProfile(base.TestCase):
conn = connection.Connection( conn = connection.Connection(
cloud='sample-cloud', cloud='sample-cloud',
vendor_hook='openstack.tests.unit.test_connection:vendor_hook' vendor_hook='openstack.tests.unit.test_connection:vendor_hook',
) )
self.assertEqual('test_val', conn.test) self.assertEqual('test_val', conn.test)
@ -492,7 +561,7 @@ class TestVendorProfile(base.TestCase):
conn = connection.Connection( conn = connection.Connection(
cloud='sample-cloud', cloud='sample-cloud',
vendor_hook='openstack.tests.unit.test_connection:missing' vendor_hook='openstack.tests.unit.test_connection:missing',
) )
self.assertIsNotNone(conn) self.assertIsNotNone(conn)

View File

@ -23,13 +23,14 @@ from openstack.tests.unit import base
class Test_Exception(base.TestCase): class Test_Exception(base.TestCase):
def test_method_not_supported(self): def test_method_not_supported(self):
exc = exceptions.MethodNotSupported(self.__class__, 'list') exc = exceptions.MethodNotSupported(self.__class__, 'list')
expected = ('The list method is not supported for ' expected = (
+ 'openstack.tests.unit.test_exceptions.Test_Exception') 'The list method is not supported for '
+ 'openstack.tests.unit.test_exceptions.Test_Exception'
)
self.assertEqual(expected, str(exc)) self.assertEqual(expected, str(exc))
class Test_HttpException(base.TestCase): class Test_HttpException(base.TestCase):
def setUp(self): def setUp(self):
super(Test_HttpException, self).setUp() super(Test_HttpException, self).setUp()
self.message = "mayday" self.message = "mayday"
@ -38,32 +39,38 @@ class Test_HttpException(base.TestCase):
raise exceptions.HttpException(*args, **kwargs) raise exceptions.HttpException(*args, **kwargs)
def test_message(self): def test_message(self):
exc = self.assertRaises(exceptions.HttpException, exc = self.assertRaises(
self._do_raise, self.message) exceptions.HttpException, self._do_raise, self.message
)
self.assertEqual(self.message, exc.message) self.assertEqual(self.message, exc.message)
def test_details(self): def test_details(self):
details = "some details" details = "some details"
exc = self.assertRaises(exceptions.HttpException, exc = self.assertRaises(
self._do_raise, self.message, exceptions.HttpException,
details=details) self._do_raise,
self.message,
details=details,
)
self.assertEqual(self.message, exc.message) self.assertEqual(self.message, exc.message)
self.assertEqual(details, exc.details) self.assertEqual(details, exc.details)
def test_http_status(self): def test_http_status(self):
http_status = 123 http_status = 123
exc = self.assertRaises(exceptions.HttpException, exc = self.assertRaises(
self._do_raise, self.message, exceptions.HttpException,
http_status=http_status) self._do_raise,
self.message,
http_status=http_status,
)
self.assertEqual(self.message, exc.message) self.assertEqual(self.message, exc.message)
self.assertEqual(http_status, exc.status_code) self.assertEqual(http_status, exc.status_code)
class TestRaiseFromResponse(base.TestCase): class TestRaiseFromResponse(base.TestCase):
def setUp(self): def setUp(self):
super(TestRaiseFromResponse, self).setUp() super(TestRaiseFromResponse, self).setUp()
self.message = "Where is my kitty?" self.message = "Where is my kitty?"
@ -83,14 +90,16 @@ class TestRaiseFromResponse(base.TestCase):
'content-type': 'application/json', 'content-type': 'application/json',
'x-openstack-request-id': uuid.uuid4().hex, 'x-openstack-request-id': uuid.uuid4().hex,
} }
exc = self.assertRaises(exceptions.NotFoundException, exc = self.assertRaises(
self._do_raise, response, exceptions.NotFoundException,
error_message=self.message) self._do_raise,
response,
error_message=self.message,
)
self.assertEqual(self.message, exc.message) self.assertEqual(self.message, exc.message)
self.assertEqual(response.status_code, exc.status_code) self.assertEqual(response.status_code, exc.status_code)
self.assertEqual( self.assertEqual(
response.headers.get('x-openstack-request-id'), response.headers.get('x-openstack-request-id'), exc.request_id
exc.request_id
) )
def test_raise_bad_request_exception(self): def test_raise_bad_request_exception(self):
@ -100,14 +109,16 @@ class TestRaiseFromResponse(base.TestCase):
'content-type': 'application/json', 'content-type': 'application/json',
'x-openstack-request-id': uuid.uuid4().hex, 'x-openstack-request-id': uuid.uuid4().hex,
} }
exc = self.assertRaises(exceptions.BadRequestException, exc = self.assertRaises(
self._do_raise, response, exceptions.BadRequestException,
error_message=self.message) self._do_raise,
response,
error_message=self.message,
)
self.assertEqual(self.message, exc.message) self.assertEqual(self.message, exc.message)
self.assertEqual(response.status_code, exc.status_code) self.assertEqual(response.status_code, exc.status_code)
self.assertEqual( self.assertEqual(
response.headers.get('x-openstack-request-id'), response.headers.get('x-openstack-request-id'), exc.request_id
exc.request_id
) )
def test_raise_http_exception(self): def test_raise_http_exception(self):
@ -117,14 +128,16 @@ class TestRaiseFromResponse(base.TestCase):
'content-type': 'application/json', 'content-type': 'application/json',
'x-openstack-request-id': uuid.uuid4().hex, 'x-openstack-request-id': uuid.uuid4().hex,
} }
exc = self.assertRaises(exceptions.HttpException, exc = self.assertRaises(
self._do_raise, response, exceptions.HttpException,
error_message=self.message) self._do_raise,
response,
error_message=self.message,
)
self.assertEqual(self.message, exc.message) self.assertEqual(self.message, exc.message)
self.assertEqual(response.status_code, exc.status_code) self.assertEqual(response.status_code, exc.status_code)
self.assertEqual( self.assertEqual(
response.headers.get('x-openstack-request-id'), response.headers.get('x-openstack-request-id'), exc.request_id
exc.request_id
) )
def test_raise_compute_format(self): def test_raise_compute_format(self):
@ -139,9 +152,12 @@ class TestRaiseFromResponse(base.TestCase):
'code': 404, 'code': 404,
} }
} }
exc = self.assertRaises(exceptions.NotFoundException, exc = self.assertRaises(
self._do_raise, response, exceptions.NotFoundException,
error_message=self.message) self._do_raise,
response,
error_message=self.message,
)
self.assertEqual(response.status_code, exc.status_code) self.assertEqual(response.status_code, exc.status_code)
self.assertEqual(self.message, exc.details) self.assertEqual(self.message, exc.details)
self.assertIn(self.message, str(exc)) self.assertIn(self.message, str(exc))
@ -159,9 +175,12 @@ class TestRaiseFromResponse(base.TestCase):
'detail': '', 'detail': '',
} }
} }
exc = self.assertRaises(exceptions.NotFoundException, exc = self.assertRaises(
self._do_raise, response, exceptions.NotFoundException,
error_message=self.message) self._do_raise,
response,
error_message=self.message,
)
self.assertEqual(response.status_code, exc.status_code) self.assertEqual(response.status_code, exc.status_code)
self.assertEqual(self.message, exc.details) self.assertEqual(self.message, exc.details)
self.assertIn(self.message, str(exc)) self.assertIn(self.message, str(exc))
@ -173,15 +192,20 @@ class TestRaiseFromResponse(base.TestCase):
'content-type': 'application/json', 'content-type': 'application/json',
} }
response.json.return_value = { response.json.return_value = {
'error_message': json.dumps({ 'error_message': json.dumps(
'faultstring': self.message, {
'faultcode': 'Client', 'faultstring': self.message,
'debuginfo': None, 'faultcode': 'Client',
}) 'debuginfo': None,
}
)
} }
exc = self.assertRaises(exceptions.NotFoundException, exc = self.assertRaises(
self._do_raise, response, exceptions.NotFoundException,
error_message=self.message) self._do_raise,
response,
error_message=self.message,
)
self.assertEqual(response.status_code, exc.status_code) self.assertEqual(response.status_code, exc.status_code)
self.assertEqual(self.message, exc.details) self.assertEqual(self.message, exc.details)
self.assertIn(self.message, str(exc)) self.assertIn(self.message, str(exc))
@ -199,9 +223,12 @@ class TestRaiseFromResponse(base.TestCase):
'debuginfo': None, 'debuginfo': None,
} }
} }
exc = self.assertRaises(exceptions.NotFoundException, exc = self.assertRaises(
self._do_raise, response, exceptions.NotFoundException,
error_message=self.message) self._do_raise,
response,
error_message=self.message,
)
self.assertEqual(response.status_code, exc.status_code) self.assertEqual(response.status_code, exc.status_code)
self.assertEqual(self.message, exc.details) self.assertEqual(self.message, exc.details)
self.assertIn(self.message, str(exc)) self.assertIn(self.message, str(exc))
@ -217,9 +244,12 @@ class TestRaiseFromResponse(base.TestCase):
'faultcode': 'Client', 'faultcode': 'Client',
'debuginfo': None, 'debuginfo': None,
} }
exc = self.assertRaises(exceptions.NotFoundException, exc = self.assertRaises(
self._do_raise, response, exceptions.NotFoundException,
error_message=self.message) self._do_raise,
response,
error_message=self.message,
)
self.assertEqual(response.status_code, exc.status_code) self.assertEqual(response.status_code, exc.status_code)
self.assertEqual(self.message, exc.details) self.assertEqual(self.message, exc.details)
self.assertIn(self.message, str(exc)) self.assertIn(self.message, str(exc))

View File

@ -15,7 +15,6 @@ from openstack.tests.unit import base
class TestBoolStrFormatter(base.TestCase): class TestBoolStrFormatter(base.TestCase):
def test_deserialize(self): def test_deserialize(self):
self.assertTrue(format.BoolStr.deserialize(True)) self.assertTrue(format.BoolStr.deserialize(True))
self.assertTrue(format.BoolStr.deserialize('True')) self.assertTrue(format.BoolStr.deserialize('True'))

View File

@ -49,12 +49,23 @@ class HackingTestCase(base.TestCase):
just assertTrue if the check is expected to fail and assertFalse if it just assertTrue if the check is expected to fail and assertFalse if it
should pass. should pass.
""" """
def test_assert_no_setupclass(self): def test_assert_no_setupclass(self):
self.assertEqual(len(list(_hacking.assert_no_setupclass( self.assertEqual(
"def setUpClass(cls)"))), 1) len(list(_hacking.assert_no_setupclass("def setUpClass(cls)"))), 1
)
self.assertEqual(len(list(_hacking.assert_no_setupclass( self.assertEqual(
"# setUpClass is evil"))), 0) len(list(_hacking.assert_no_setupclass("# setUpClass is evil"))), 0
)
self.assertEqual(len(list(_hacking.assert_no_setupclass( self.assertEqual(
"def setUpClassyDrinkingLocation(cls)"))), 0) len(
list(
_hacking.assert_no_setupclass(
"def setUpClassyDrinkingLocation(cls)"
)
)
),
0,
)

View File

@ -16,7 +16,6 @@ from openstack.tests.unit import base
class TestMicroversions(base.TestCase): class TestMicroversions(base.TestCase):
def setUp(self): def setUp(self):
super(TestMicroversions, self).setUp() super(TestMicroversions, self).setUp()
self.use_compute_discovery() self.use_compute_discovery()
@ -27,7 +26,8 @@ class TestMicroversions(base.TestCase):
self.assertRaises( self.assertRaises(
exceptions.ConfigException, exceptions.ConfigException,
self.cloud.get_server, 'doesNotExist', self.cloud.get_server,
'doesNotExist',
) )
self.assert_calls() self.assert_calls()
@ -38,7 +38,8 @@ class TestMicroversions(base.TestCase):
self.assertRaises( self.assertRaises(
exceptions.ConfigException, exceptions.ConfigException,
self.cloud.get_server, 'doesNotExist', self.cloud.get_server,
'doesNotExist',
) )
self.assert_calls() self.assert_calls()
@ -49,7 +50,8 @@ class TestMicroversions(base.TestCase):
self.assertRaises( self.assertRaises(
exceptions.ConfigException, exceptions.ConfigException,
self.cloud.get_server, 'doesNotExist', self.cloud.get_server,
'doesNotExist',
) )
self.assert_calls() self.assert_calls()
@ -60,7 +62,8 @@ class TestMicroversions(base.TestCase):
self.assertRaises( self.assertRaises(
exceptions.ConfigException, exceptions.ConfigException,
self.cloud.get_server, 'doesNotExist', self.cloud.get_server,
'doesNotExist',
) )
self.assert_calls() self.assert_calls()
@ -72,13 +75,18 @@ class TestMicroversions(base.TestCase):
server1 = fakes.make_fake_server('123', 'mickey') server1 = fakes.make_fake_server('123', 'mickey')
server2 = fakes.make_fake_server('345', 'mouse') server2 = fakes.make_fake_server('345', 'mouse')
self.register_uris([ self.register_uris(
dict(method='GET', [
uri=self.get_mock_url( dict(
'compute', 'public', append=['servers', 'detail']), method='GET',
request_headers={'OpenStack-API-Version': 'compute 2.42'}, uri=self.get_mock_url(
json={'servers': [server1, server2]}), 'compute', 'public', append=['servers', 'detail']
]) ),
request_headers={'OpenStack-API-Version': 'compute 2.42'},
json={'servers': [server1, server2]},
),
]
)
r = self.cloud.get_server('mickey', bare=True) r = self.cloud.get_server('mickey', bare=True)
self.assertIsNotNone(r) self.assertIsNotNone(r)
@ -93,13 +101,18 @@ class TestMicroversions(base.TestCase):
server1 = fakes.make_fake_server('123', 'mickey') server1 = fakes.make_fake_server('123', 'mickey')
server2 = fakes.make_fake_server('345', 'mouse') server2 = fakes.make_fake_server('345', 'mouse')
self.register_uris([ self.register_uris(
dict(method='GET', [
uri=self.get_mock_url( dict(
'compute', 'public', append=['servers', 'detail']), method='GET',
request_headers={'OpenStack-API-Version': 'compute 2.42'}, uri=self.get_mock_url(
json={'servers': [server1, server2]}), 'compute', 'public', append=['servers', 'detail']
]) ),
request_headers={'OpenStack-API-Version': 'compute 2.42'},
json={'servers': [server1, server2]},
),
]
)
r = self.cloud.get_server('mickey', bare=True) r = self.cloud.get_server('mickey', bare=True)
self.assertIsNotNone(r) self.assertIsNotNone(r)

View File

@ -18,18 +18,20 @@ from openstack.tests.unit import base
class TestMissingVersion(base.TestCase): class TestMissingVersion(base.TestCase):
def setUp(self): def setUp(self):
super(TestMissingVersion, self).setUp() super(TestMissingVersion, self).setUp()
self.os_fixture.clear_tokens() self.os_fixture.clear_tokens()
svc = self.os_fixture.v3_token.add_service('image') svc = self.os_fixture.v3_token.add_service('image')
svc.add_endpoint( svc.add_endpoint(
url='https://example.com/image/', url='https://example.com/image/',
region='RegionOne', interface='public') region='RegionOne',
interface='public',
)
self.use_keystone_v3() self.use_keystone_v3()
self.use_glance( self.use_glance(
image_version_json='bad-glance-version.json', image_version_json='bad-glance-version.json',
image_discovery_url='https://example.com/image/') image_discovery_url='https://example.com/image/',
)
def test_unsupported_version(self): def test_unsupported_version(self):

View File

@ -20,7 +20,6 @@ from openstack.tests.unit import base
@ddt.ddt @ddt.ddt
class TestPlacementRest(base.TestCase): class TestPlacementRest(base.TestCase):
def setUp(self): def setUp(self):
super(TestPlacementRest, self).setUp() super(TestPlacementRest, self).setUp()
self.use_placement() self.use_placement()
@ -29,8 +28,10 @@ class TestPlacementRest(base.TestCase):
uri = dict( uri = dict(
method='GET', method='GET',
uri=self.get_mock_url( uri=self.get_mock_url(
'placement', 'public', append=['allocation_candidates']), 'placement', 'public', append=['allocation_candidates']
json={}) ),
json={},
)
if status_code is not None: if status_code is not None:
uri['status_code'] = status_code uri['status_code'] = status_code
self.register_uris([uri]) self.register_uris([uri])
@ -38,8 +39,8 @@ class TestPlacementRest(base.TestCase):
def _validate_resp(self, resp, status_code): def _validate_resp(self, resp, status_code):
self.assertEqual(status_code, resp.status_code) self.assertEqual(status_code, resp.status_code)
self.assertEqual( self.assertEqual(
'https://placement.example.com/allocation_candidates', 'https://placement.example.com/allocation_candidates', resp.url
resp.url) )
self.assert_calls() self.assert_calls()
@ddt.data({}, {'raise_exc': False}, {'raise_exc': True}) @ddt.data({}, {'raise_exc': False}, {'raise_exc': True})
@ -61,18 +62,20 @@ class TestPlacementRest(base.TestCase):
# raise_exc=True raises a ksa exception appropriate to the status code # raise_exc=True raises a ksa exception appropriate to the status code
ex = self.assertRaises( ex = self.assertRaises(
exceptions.InternalServerError, exceptions.InternalServerError,
self.cloud.placement.get, '/allocation_candidates', raise_exc=True) self.cloud.placement.get,
'/allocation_candidates',
raise_exc=True,
)
self._validate_resp(ex.response, 500) self._validate_resp(ex.response, 500)
def test_microversion_discovery(self): def test_microversion_discovery(self):
self.assertEqual( self.assertEqual(
(1, 17), (1, 17), self.cloud.placement.get_endpoint_data().max_microversion
self.cloud.placement.get_endpoint_data().max_microversion) )
self.assert_calls() self.assert_calls()
class TestBadPlacementRest(base.TestCase): class TestBadPlacementRest(base.TestCase):
def setUp(self): def setUp(self):
self.skipTest('Need to re-add support for broken placement versions') self.skipTest('Need to re-add support for broken placement versions')
super(TestBadPlacementRest, self).setUp() super(TestBadPlacementRest, self).setUp()
@ -85,8 +88,10 @@ class TestBadPlacementRest(base.TestCase):
uri = dict( uri = dict(
method='GET', method='GET',
uri=self.get_mock_url( uri=self.get_mock_url(
'placement', 'public', append=['allocation_candidates']), 'placement', 'public', append=['allocation_candidates']
json={}) ),
json={},
)
if status_code is not None: if status_code is not None:
uri['status_code'] = status_code uri['status_code'] = status_code
self.register_uris([uri]) self.register_uris([uri])
@ -94,8 +99,8 @@ class TestBadPlacementRest(base.TestCase):
def _validate_resp(self, resp, status_code): def _validate_resp(self, resp, status_code):
self.assertEqual(status_code, resp.status_code) self.assertEqual(status_code, resp.status_code)
self.assertEqual( self.assertEqual(
'https://placement.example.com/allocation_candidates', 'https://placement.example.com/allocation_candidates', resp.url
resp.url) )
self.assert_calls() self.assert_calls()
def test_discovery(self): def test_discovery(self):

View File

@ -58,7 +58,6 @@ class HeadableResource(resource.Resource):
class TestProxyPrivate(base.TestCase): class TestProxyPrivate(base.TestCase):
def setUp(self): def setUp(self):
super(TestProxyPrivate, self).setUp() super(TestProxyPrivate, self).setUp()
@ -88,9 +87,14 @@ class TestProxyPrivate(base.TestCase):
def test__check_resource_strict_id(self): def test__check_resource_strict_id(self):
decorated = proxy._check_resource(strict=True)(self.sot.method) decorated = proxy._check_resource(strict=True)(self.sot.method)
self.assertRaisesRegex(ValueError, "A Resource must be passed", self.assertRaisesRegex(
decorated, self.sot, resource.Resource, ValueError,
"this-is-not-a-resource") "A Resource must be passed",
decorated,
self.sot,
resource.Resource,
"this-is-not-a-resource",
)
def test__check_resource_incorrect_resource(self): def test__check_resource_incorrect_resource(self):
class OneType(resource.Resource): class OneType(resource.Resource):
@ -101,9 +105,14 @@ class TestProxyPrivate(base.TestCase):
value = AnotherType() value = AnotherType()
decorated = proxy._check_resource(strict=False)(self.sot.method) decorated = proxy._check_resource(strict=False)(self.sot.method)
self.assertRaisesRegex(ValueError, self.assertRaisesRegex(
"Expected OneType but received AnotherType", ValueError,
decorated, self.sot, OneType, value) "Expected OneType but received AnotherType",
decorated,
self.sot,
OneType,
value,
)
def test__get_uri_attribute_no_parent(self): def test__get_uri_attribute_no_parent(self):
class Child(resource.Resource): class Child(resource.Resource):
@ -161,7 +170,8 @@ class TestProxyPrivate(base.TestCase):
result = self.fake_proxy._get_resource(Fake, id, **attrs) result = self.fake_proxy._get_resource(Fake, id, **attrs)
self.assertDictEqual( self.assertDictEqual(
dict(id=id, connection=mock.ANY, **attrs), Fake.call) dict(id=id, connection=mock.ANY, **attrs), Fake.call
)
self.assertEqual(value, result) self.assertEqual(value, result)
def test__get_resource_from_resource(self): def test__get_resource_from_resource(self):
@ -170,8 +180,7 @@ class TestProxyPrivate(base.TestCase):
attrs = {"first": "Brian", "last": "Curtin"} attrs = {"first": "Brian", "last": "Curtin"}
result = self.fake_proxy._get_resource(resource.Resource, result = self.fake_proxy._get_resource(resource.Resource, res, **attrs)
res, **attrs)
res._update.assert_called_once_with(**attrs) res._update.assert_called_once_with(**attrs)
self.assertEqual(result, res) self.assertEqual(result, res)
@ -193,7 +202,6 @@ class TestProxyPrivate(base.TestCase):
class TestProxyDelete(base.TestCase): class TestProxyDelete(base.TestCase):
def setUp(self): def setUp(self):
super(TestProxyDelete, self).setUp() super(TestProxyDelete, self).setUp()
@ -215,7 +223,8 @@ class TestProxyDelete(base.TestCase):
self.sot._delete(DeleteableResource, self.fake_id) self.sot._delete(DeleteableResource, self.fake_id)
DeleteableResource.new.assert_called_with( DeleteableResource.new.assert_called_with(
connection=self.cloud, id=self.fake_id) connection=self.cloud, id=self.fake_id
)
self.res.delete.assert_called_with(self.sot) self.res.delete.assert_called_with(self.sot)
# Delete generally doesn't return anything, so we will normally # Delete generally doesn't return anything, so we will normally
@ -227,32 +236,42 @@ class TestProxyDelete(base.TestCase):
def test_delete_ignore_missing(self): def test_delete_ignore_missing(self):
self.res.delete.side_effect = exceptions.ResourceNotFound( self.res.delete.side_effect = exceptions.ResourceNotFound(
message="test", http_status=404) message="test", http_status=404
)
rv = self.sot._delete(DeleteableResource, self.fake_id) rv = self.sot._delete(DeleteableResource, self.fake_id)
self.assertIsNone(rv) self.assertIsNone(rv)
def test_delete_NotFound(self): def test_delete_NotFound(self):
self.res.delete.side_effect = exceptions.ResourceNotFound( self.res.delete.side_effect = exceptions.ResourceNotFound(
message="test", http_status=404) message="test", http_status=404
)
self.assertRaisesRegex( self.assertRaisesRegex(
exceptions.ResourceNotFound, exceptions.ResourceNotFound,
# TODO(shade) The mocks here are hiding the thing we want to test. # TODO(shade) The mocks here are hiding the thing we want to test.
"test", "test",
self.sot._delete, DeleteableResource, self.res, self.sot._delete,
ignore_missing=False) DeleteableResource,
self.res,
ignore_missing=False,
)
def test_delete_HttpException(self): def test_delete_HttpException(self):
self.res.delete.side_effect = exceptions.HttpException( self.res.delete.side_effect = exceptions.HttpException(
message="test", http_status=500) message="test", http_status=500
)
self.assertRaises(exceptions.HttpException, self.sot._delete, self.assertRaises(
DeleteableResource, self.res, ignore_missing=False) exceptions.HttpException,
self.sot._delete,
DeleteableResource,
self.res,
ignore_missing=False,
)
class TestProxyUpdate(base.TestCase): class TestProxyUpdate(base.TestCase):
def setUp(self): def setUp(self):
super(TestProxyUpdate, self).setUp() super(TestProxyUpdate, self).setUp()
@ -280,8 +299,9 @@ class TestProxyUpdate(base.TestCase):
def test_update_resource_override_base_path(self): def test_update_resource_override_base_path(self):
base_path = 'dummy' base_path = 'dummy'
rv = self.sot._update(UpdateableResource, self.res, rv = self.sot._update(
base_path=base_path, **self.attrs) UpdateableResource, self.res, base_path=base_path, **self.attrs
)
self.assertEqual(rv, self.fake_result) self.assertEqual(rv, self.fake_result)
self.res._update.assert_called_once_with(**self.attrs) self.res._update.assert_called_once_with(**self.attrs)
@ -295,7 +315,6 @@ class TestProxyUpdate(base.TestCase):
class TestProxyCreate(base.TestCase): class TestProxyCreate(base.TestCase):
def setUp(self): def setUp(self):
super(TestProxyCreate, self).setUp() super(TestProxyCreate, self).setUp()
@ -317,7 +336,8 @@ class TestProxyCreate(base.TestCase):
self.assertEqual(rv, self.fake_result) self.assertEqual(rv, self.fake_result)
CreateableResource.new.assert_called_once_with( CreateableResource.new.assert_called_once_with(
connection=self.cloud, **attrs) connection=self.cloud, **attrs
)
self.res.create.assert_called_once_with(self.sot, base_path=None) self.res.create.assert_called_once_with(self.sot, base_path=None)
def test_create_attributes_override_base_path(self): def test_create_attributes_override_base_path(self):
@ -329,12 +349,12 @@ class TestProxyCreate(base.TestCase):
self.assertEqual(rv, self.fake_result) self.assertEqual(rv, self.fake_result)
CreateableResource.new.assert_called_once_with( CreateableResource.new.assert_called_once_with(
connection=self.cloud, **attrs) connection=self.cloud, **attrs
)
self.res.create.assert_called_once_with(self.sot, base_path=base_path) self.res.create.assert_called_once_with(self.sot, base_path=base_path)
class TestProxyBulkCreate(base.TestCase): class TestProxyBulkCreate(base.TestCase):
def setUp(self): def setUp(self):
super(TestProxyBulkCreate, self).setUp() super(TestProxyBulkCreate, self).setUp()
@ -353,8 +373,9 @@ class TestProxyBulkCreate(base.TestCase):
rv = self.sot._bulk_create(self.cls, self.data) rv = self.sot._bulk_create(self.cls, self.data)
self.assertEqual(rv, self.result) self.assertEqual(rv, self.result)
self.cls.bulk_create.assert_called_once_with(self.sot, self.data, self.cls.bulk_create.assert_called_once_with(
base_path=None) self.sot, self.data, base_path=None
)
def test_bulk_create_attributes_override_base_path(self): def test_bulk_create_attributes_override_base_path(self):
base_path = 'dummy' base_path = 'dummy'
@ -362,12 +383,12 @@ class TestProxyBulkCreate(base.TestCase):
rv = self.sot._bulk_create(self.cls, self.data, base_path=base_path) rv = self.sot._bulk_create(self.cls, self.data, base_path=base_path)
self.assertEqual(rv, self.result) self.assertEqual(rv, self.result)
self.cls.bulk_create.assert_called_once_with(self.sot, self.data, self.cls.bulk_create.assert_called_once_with(
base_path=base_path) self.sot, self.data, base_path=base_path
)
class TestProxyGet(base.TestCase): class TestProxyGet(base.TestCase):
def setUp(self): def setUp(self):
super(TestProxyGet, self).setUp() super(TestProxyGet, self).setUp()
@ -389,10 +410,12 @@ class TestProxyGet(base.TestCase):
rv = self.sot._get(RetrieveableResource, self.res) rv = self.sot._get(RetrieveableResource, self.res)
self.res.fetch.assert_called_with( self.res.fetch.assert_called_with(
self.sot, requires_id=True, self.sot,
requires_id=True,
base_path=None, base_path=None,
skip_cache=mock.ANY, skip_cache=mock.ANY,
error_message=mock.ANY) error_message=mock.ANY,
)
self.assertEqual(rv, self.fake_result) self.assertEqual(rv, self.fake_result)
def test_get_resource_with_args(self): def test_get_resource_with_args(self):
@ -401,46 +424,62 @@ class TestProxyGet(base.TestCase):
self.res._update.assert_called_once_with(**args) self.res._update.assert_called_once_with(**args)
self.res.fetch.assert_called_with( self.res.fetch.assert_called_with(
self.sot, requires_id=True, base_path=None, self.sot,
requires_id=True,
base_path=None,
skip_cache=mock.ANY, skip_cache=mock.ANY,
error_message=mock.ANY) error_message=mock.ANY,
)
self.assertEqual(rv, self.fake_result) self.assertEqual(rv, self.fake_result)
def test_get_id(self): def test_get_id(self):
rv = self.sot._get(RetrieveableResource, self.fake_id) rv = self.sot._get(RetrieveableResource, self.fake_id)
RetrieveableResource.new.assert_called_with( RetrieveableResource.new.assert_called_with(
connection=self.cloud, id=self.fake_id) connection=self.cloud, id=self.fake_id
)
self.res.fetch.assert_called_with( self.res.fetch.assert_called_with(
self.sot, requires_id=True, base_path=None, self.sot,
requires_id=True,
base_path=None,
skip_cache=mock.ANY, skip_cache=mock.ANY,
error_message=mock.ANY) error_message=mock.ANY,
)
self.assertEqual(rv, self.fake_result) self.assertEqual(rv, self.fake_result)
def test_get_base_path(self): def test_get_base_path(self):
base_path = 'dummy' base_path = 'dummy'
rv = self.sot._get(RetrieveableResource, self.fake_id, rv = self.sot._get(
base_path=base_path) RetrieveableResource, self.fake_id, base_path=base_path
)
RetrieveableResource.new.assert_called_with( RetrieveableResource.new.assert_called_with(
connection=self.cloud, id=self.fake_id) connection=self.cloud, id=self.fake_id
)
self.res.fetch.assert_called_with( self.res.fetch.assert_called_with(
self.sot, requires_id=True, base_path=base_path, self.sot,
requires_id=True,
base_path=base_path,
skip_cache=mock.ANY, skip_cache=mock.ANY,
error_message=mock.ANY) error_message=mock.ANY,
)
self.assertEqual(rv, self.fake_result) self.assertEqual(rv, self.fake_result)
def test_get_not_found(self): def test_get_not_found(self):
self.res.fetch.side_effect = exceptions.ResourceNotFound( self.res.fetch.side_effect = exceptions.ResourceNotFound(
message="test", http_status=404) message="test", http_status=404
)
self.assertRaisesRegex( self.assertRaisesRegex(
exceptions.ResourceNotFound, exceptions.ResourceNotFound,
"test", self.sot._get, RetrieveableResource, self.res) "test",
self.sot._get,
RetrieveableResource,
self.res,
)
class TestProxyList(base.TestCase): class TestProxyList(base.TestCase):
def setUp(self): def setUp(self):
super(TestProxyList, self).setUp() super(TestProxyList, self).setUp()
@ -455,12 +494,17 @@ class TestProxyList(base.TestCase):
ListableResource.list.return_value = self.fake_response ListableResource.list.return_value = self.fake_response
def _test_list(self, paginated, base_path=None): def _test_list(self, paginated, base_path=None):
rv = self.sot._list(ListableResource, paginated=paginated, rv = self.sot._list(
base_path=base_path, **self.args) ListableResource,
paginated=paginated,
base_path=base_path,
**self.args,
)
self.assertEqual(self.fake_response, rv) self.assertEqual(self.fake_response, rv)
ListableResource.list.assert_called_once_with( ListableResource.list.assert_called_once_with(
self.sot, paginated=paginated, base_path=base_path, **self.args) self.sot, paginated=paginated, base_path=base_path, **self.args
)
def test_list_paginated(self): def test_list_paginated(self):
self._test_list(True) self._test_list(True)
@ -481,21 +525,24 @@ class TestProxyList(base.TestCase):
FilterableResource.list.return_value = fake_response FilterableResource.list.return_value = fake_response
rv = self.sot._list( rv = self.sot._list(
FilterableResource, paginated=False, FilterableResource,
base_path=None, jmespath_filters="[?c=='c']" paginated=False,
base_path=None,
jmespath_filters="[?c=='c']",
) )
self.assertEqual(3, len(rv)) self.assertEqual(3, len(rv))
# Test filtering based on unknown attribute # Test filtering based on unknown attribute
rv = self.sot._list( rv = self.sot._list(
FilterableResource, paginated=False, FilterableResource,
base_path=None, jmespath_filters="[?d=='c']" paginated=False,
base_path=None,
jmespath_filters="[?d=='c']",
) )
self.assertEqual(0, len(rv)) self.assertEqual(0, len(rv))
class TestProxyHead(base.TestCase): class TestProxyHead(base.TestCase):
def setUp(self): def setUp(self):
super(TestProxyHead, self).setUp() super(TestProxyHead, self).setUp()
@ -530,7 +577,8 @@ class TestProxyHead(base.TestCase):
rv = self.sot._head(HeadableResource, self.fake_id) rv = self.sot._head(HeadableResource, self.fake_id)
HeadableResource.new.assert_called_with( HeadableResource.new.assert_called_with(
connection=self.cloud, id=self.fake_id) connection=self.cloud, id=self.fake_id
)
self.res.head.assert_called_with(self.sot, base_path=None) self.res.head.assert_called_with(self.sot, base_path=None)
self.assertEqual(rv, self.fake_result) self.assertEqual(rv, self.fake_result)
@ -546,10 +594,14 @@ class TestExtractName(base.TestCase):
('networks_arg', dict(url='/v2.0/networks/1', parts=['network'])), ('networks_arg', dict(url='/v2.0/networks/1', parts=['network'])),
('tokens', dict(url='/v3/tokens', parts=['tokens'])), ('tokens', dict(url='/v3/tokens', parts=['tokens'])),
('discovery', dict(url='/', parts=['discovery'])), ('discovery', dict(url='/', parts=['discovery'])),
('secgroups', dict( (
url='/servers/1/os-security-groups', 'secgroups',
parts=['server', 'os-security-groups'])), dict(
('bm_chassis', dict(url='/v1/chassis/id', parts=['chassis'])) url='/servers/1/os-security-groups',
parts=['server', 'os-security-groups'],
),
),
('bm_chassis', dict(url='/v1/chassis/id', parts=['chassis'])),
] ]
def test_extract_name(self): def test_extract_name(self):
@ -559,7 +611,6 @@ class TestExtractName(base.TestCase):
class TestProxyCache(base.TestCase): class TestProxyCache(base.TestCase):
class Res(resource.Resource): class Res(resource.Resource):
base_path = 'fake' base_path = 'fake'
@ -570,7 +621,8 @@ class TestProxyCache(base.TestCase):
def setUp(self): def setUp(self):
super(TestProxyCache, self).setUp( super(TestProxyCache, self).setUp(
cloud_config_fixture='clouds_cache.yaml') cloud_config_fixture='clouds_cache.yaml'
)
self.session = mock.Mock() self.session = mock.Mock()
self.session._sdk_connection = self.cloud self.session._sdk_connection = self.cloud
@ -581,19 +633,15 @@ class TestProxyCache(base.TestCase):
self.response.history = [] self.response.history = []
self.response.headers = {} self.response.headers = {}
self.response.body = {} self.response.body = {}
self.response.json = mock.Mock( self.response.json = mock.Mock(return_value=self.response.body)
return_value=self.response.body) self.session.request = mock.Mock(return_value=self.response)
self.session.request = mock.Mock(
return_value=self.response)
self.sot = proxy.Proxy(self.session) self.sot = proxy.Proxy(self.session)
self.sot._connection = self.cloud self.sot._connection = self.cloud
self.sot.service_type = 'srv' self.sot.service_type = 'srv'
def _get_key(self, id): def _get_key(self, id):
return ( return f"srv.fake.fake/{id}." "{'microversion': None, 'params': {}}"
f"srv.fake.fake/{id}."
"{'microversion': None, 'params': {}}")
def test_get_not_in_cache(self): def test_get_not_in_cache(self):
self.cloud._cache_expirations['srv.fake'] = 5 self.cloud._cache_expirations['srv.fake'] = 5
@ -602,15 +650,15 @@ class TestProxyCache(base.TestCase):
self.session.request.assert_called_with( self.session.request.assert_called_with(
'fake/1', 'fake/1',
'GET', 'GET',
connect_retries=mock.ANY, raise_exc=mock.ANY, connect_retries=mock.ANY,
raise_exc=mock.ANY,
global_request_id=mock.ANY, global_request_id=mock.ANY,
endpoint_filter=mock.ANY, endpoint_filter=mock.ANY,
headers=mock.ANY, headers=mock.ANY,
microversion=mock.ANY, params=mock.ANY microversion=mock.ANY,
params=mock.ANY,
) )
self.assertIn( self.assertIn(self._get_key(1), self.cloud._api_cache_keys)
self._get_key(1),
self.cloud._api_cache_keys)
def test_get_from_cache(self): def test_get_from_cache(self):
key = self._get_key(2) key = self._get_key(2)
@ -639,9 +687,7 @@ class TestProxyCache(base.TestCase):
self.session.request.assert_called() self.session.request.assert_called()
self.assertIsNotNone(self.cloud._cache.get(key)) self.assertIsNotNone(self.cloud._cache.get(key))
self.assertEqual( self.assertEqual('NoValue', type(self.cloud._cache.get(key)).__name__)
'NoValue',
type(self.cloud._cache.get(key)).__name__)
self.assertNotIn(key, self.cloud._api_cache_keys) self.assertNotIn(key, self.cloud._api_cache_keys)
# next get call again triggers API # next get call again triggers API
@ -663,13 +709,10 @@ class TestProxyCache(base.TestCase):
# validate we got empty body as expected, and not what is in cache # validate we got empty body as expected, and not what is in cache
self.assertEqual(dict(), self.response.body) self.assertEqual(dict(), self.response.body)
self.assertNotIn(key, self.cloud._api_cache_keys) self.assertNotIn(key, self.cloud._api_cache_keys)
self.assertEqual( self.assertEqual('NoValue', type(self.cloud._cache.get(key)).__name__)
'NoValue',
type(self.cloud._cache.get(key)).__name__)
class TestProxyCleanup(base.TestCase): class TestProxyCleanup(base.TestCase):
def setUp(self): def setUp(self):
super(TestProxyCleanup, self).setUp() super(TestProxyCleanup, self).setUp()
@ -693,40 +736,28 @@ class TestProxyCleanup(base.TestCase):
def test_filters_evaluation_created_at(self): def test_filters_evaluation_created_at(self):
self.assertTrue( self.assertTrue(
self.sot._service_cleanup_resource_filters_evaluation( self.sot._service_cleanup_resource_filters_evaluation(
self.res, self.res, filters={'created_at': '2020-02-03T00:00:00'}
filters={
'created_at': '2020-02-03T00:00:00'
}
) )
) )
def test_filters_evaluation_created_at_not(self): def test_filters_evaluation_created_at_not(self):
self.assertFalse( self.assertFalse(
self.sot._service_cleanup_resource_filters_evaluation( self.sot._service_cleanup_resource_filters_evaluation(
self.res, self.res, filters={'created_at': '2020-01-01T00:00:00'}
filters={
'created_at': '2020-01-01T00:00:00'
}
) )
) )
def test_filters_evaluation_updated_at(self): def test_filters_evaluation_updated_at(self):
self.assertTrue( self.assertTrue(
self.sot._service_cleanup_resource_filters_evaluation( self.sot._service_cleanup_resource_filters_evaluation(
self.res, self.res, filters={'updated_at': '2020-02-03T00:00:00'}
filters={
'updated_at': '2020-02-03T00:00:00'
}
) )
) )
def test_filters_evaluation_updated_at_not(self): def test_filters_evaluation_updated_at_not(self):
self.assertFalse( self.assertFalse(
self.sot._service_cleanup_resource_filters_evaluation( self.sot._service_cleanup_resource_filters_evaluation(
self.res, self.res, filters={'updated_at': '2020-01-01T00:00:00'}
filters={
'updated_at': '2020-01-01T00:00:00'
}
) )
) )
@ -734,9 +765,7 @@ class TestProxyCleanup(base.TestCase):
self.assertFalse( self.assertFalse(
self.sot._service_cleanup_resource_filters_evaluation( self.sot._service_cleanup_resource_filters_evaluation(
self.res_no_updated, self.res_no_updated,
filters={ filters={'updated_at': '2020-01-01T00:00:00'},
'updated_at': '2020-01-01T00:00:00'
}
) )
) )
@ -750,19 +779,14 @@ class TestProxyCleanup(base.TestCase):
def test_service_cleanup_dry_run(self): def test_service_cleanup_dry_run(self):
self.assertTrue( self.assertTrue(
self.sot._service_cleanup_del_res( self.sot._service_cleanup_del_res(
self.delete_mock, self.delete_mock, self.res, dry_run=True
self.res,
dry_run=True
) )
) )
self.delete_mock.assert_not_called() self.delete_mock.assert_not_called()
def test_service_cleanup_dry_run_default(self): def test_service_cleanup_dry_run_default(self):
self.assertTrue( self.assertTrue(
self.sot._service_cleanup_del_res( self.sot._service_cleanup_del_res(self.delete_mock, self.res)
self.delete_mock,
self.res
)
) )
self.delete_mock.assert_not_called() self.delete_mock.assert_not_called()
@ -783,7 +807,7 @@ class TestProxyCleanup(base.TestCase):
self.delete_mock, self.delete_mock,
self.res, self.res,
dry_run=False, dry_run=False,
identified_resources=rd identified_resources=rd,
) )
) )
self.delete_mock.assert_called_with(self.res) self.delete_mock.assert_called_with(self.res)
@ -795,7 +819,7 @@ class TestProxyCleanup(base.TestCase):
self.delete_mock, self.delete_mock,
self.res, self.res,
dry_run=False, dry_run=False,
resource_evaluation_fn=lambda x, y, z: False resource_evaluation_fn=lambda x, y, z: False,
) )
) )
self.delete_mock.assert_not_called() self.delete_mock.assert_not_called()
@ -806,7 +830,7 @@ class TestProxyCleanup(base.TestCase):
self.delete_mock, self.delete_mock,
self.res, self.res,
dry_run=False, dry_run=False,
resource_evaluation_fn=lambda x, y, z: True resource_evaluation_fn=lambda x, y, z: True,
) )
) )
self.delete_mock.assert_called() self.delete_mock.assert_called()
@ -818,7 +842,7 @@ class TestProxyCleanup(base.TestCase):
self.res, self.res,
dry_run=False, dry_run=False,
resource_evaluation_fn=lambda x, y, z: False, resource_evaluation_fn=lambda x, y, z: False,
filters={'created_at': '2200-01-01'} filters={'created_at': '2200-01-01'},
) )
) )
@ -828,7 +852,7 @@ class TestProxyCleanup(base.TestCase):
self.delete_mock, self.delete_mock,
self.res, self.res,
dry_run=False, dry_run=False,
filters={'created_at': '2200-01-01'} filters={'created_at': '2200-01-01'},
) )
) )
self.delete_mock.assert_called() self.delete_mock.assert_called()
@ -841,7 +865,7 @@ class TestProxyCleanup(base.TestCase):
self.res, self.res,
dry_run=False, dry_run=False,
client_status_queue=q, client_status_queue=q,
filters={'created_at': '2200-01-01'} filters={'created_at': '2200-01-01'},
) )
) )
self.assertEqual(self.res, q.get_nowait()) self.assertEqual(self.res, q.get_nowait())

View File

@ -21,18 +21,27 @@ class TestProxyBase(base.TestCase):
self.session = mock.Mock() self.session = mock.Mock()
def _verify( def _verify(
self, mock_method, test_method, *, self,
method_args=None, method_kwargs=None, method_result=None, mock_method,
expected_args=None, expected_kwargs=None, expected_result=None, test_method,
*,
method_args=None,
method_kwargs=None,
method_result=None,
expected_args=None,
expected_kwargs=None,
expected_result=None,
): ):
with mock.patch(mock_method) as mocked: with mock.patch(mock_method) as mocked:
mocked.return_value = expected_result mocked.return_value = expected_result
if any([ if any(
method_args, [
method_kwargs, method_args,
expected_args, method_kwargs,
expected_kwargs, expected_args,
]): expected_kwargs,
]
):
method_args = method_args or () method_args = method_args or ()
method_kwargs = method_kwargs or {} method_kwargs = method_kwargs or {}
expected_args = expected_args or () expected_args = expected_args or ()
@ -77,9 +86,16 @@ class TestProxyBase(base.TestCase):
mocked.assert_called_with(test_method.__self__) mocked.assert_called_with(test_method.__self__)
def verify_create( def verify_create(
self, test_method, resource_type, base_path=None, *, self,
method_args=None, method_kwargs=None, test_method,
expected_args=None, expected_kwargs=None, expected_result="result", resource_type,
base_path=None,
*,
method_args=None,
method_kwargs=None,
expected_args=None,
expected_kwargs=None,
expected_result="result",
mock_method="openstack.proxy.Proxy._create", mock_method="openstack.proxy.Proxy._create",
): ):
if method_args is None: if method_args is None:
@ -103,9 +119,15 @@ class TestProxyBase(base.TestCase):
) )
def verify_delete( def verify_delete(
self, test_method, resource_type, ignore_missing=True, *, self,
method_args=None, method_kwargs=None, test_method,
expected_args=None, expected_kwargs=None, resource_type,
ignore_missing=True,
*,
method_args=None,
method_kwargs=None,
expected_args=None,
expected_kwargs=None,
mock_method="openstack.proxy.Proxy._delete", mock_method="openstack.proxy.Proxy._delete",
): ):
if method_args is None: if method_args is None:
@ -128,9 +150,16 @@ class TestProxyBase(base.TestCase):
) )
def verify_get( def verify_get(
self, test_method, resource_type, requires_id=False, base_path=None, *, self,
method_args=None, method_kwargs=None, test_method,
expected_args=None, expected_kwargs=None, resource_type,
requires_id=False,
base_path=None,
*,
method_args=None,
method_kwargs=None,
expected_args=None,
expected_kwargs=None,
mock_method="openstack.proxy.Proxy._get", mock_method="openstack.proxy.Proxy._get",
): ):
if method_args is None: if method_args is None:
@ -156,15 +185,23 @@ class TestProxyBase(base.TestCase):
proxy._get_resource = mock.Mock(return_value=res) proxy._get_resource = mock.Mock(return_value=res)
proxy._get(resource_type) proxy._get(resource_type)
res.fetch.assert_called_once_with( res.fetch.assert_called_once_with(
proxy, requires_id=True, proxy,
requires_id=True,
base_path=None, base_path=None,
error_message=mock.ANY, error_message=mock.ANY,
skip_cache=False) skip_cache=False,
)
def verify_head( def verify_head(
self, test_method, resource_type, base_path=None, *, self,
method_args=None, method_kwargs=None, test_method,
expected_args=None, expected_kwargs=None, resource_type,
base_path=None,
*,
method_args=None,
method_kwargs=None,
expected_args=None,
expected_kwargs=None,
mock_method="openstack.proxy.Proxy._head", mock_method="openstack.proxy.Proxy._head",
): ):
if method_args is None: if method_args is None:
@ -184,10 +221,16 @@ class TestProxyBase(base.TestCase):
) )
def verify_find( def verify_find(
self, test_method, resource_type, name_or_id='resource_name', self,
ignore_missing=True, *, test_method,
method_args=None, method_kwargs=None, resource_type,
expected_args=None, expected_kwargs=None, name_or_id='resource_name',
ignore_missing=True,
*,
method_args=None,
method_kwargs=None,
expected_args=None,
expected_kwargs=None,
mock_method="openstack.proxy.Proxy._find", mock_method="openstack.proxy.Proxy._find",
): ):
method_args = [name_or_id] + (method_args or []) method_args = [name_or_id] + (method_args or [])
@ -206,9 +249,16 @@ class TestProxyBase(base.TestCase):
) )
def verify_list( def verify_list(
self, test_method, resource_type, paginated=None, base_path=None, *, self,
method_args=None, method_kwargs=None, test_method,
expected_args=None, expected_kwargs=None, resource_type,
paginated=None,
base_path=None,
*,
method_args=None,
method_kwargs=None,
expected_args=None,
expected_kwargs=None,
mock_method="openstack.proxy.Proxy._list", mock_method="openstack.proxy.Proxy._list",
): ):
if method_args is None: if method_args is None:
@ -234,9 +284,16 @@ class TestProxyBase(base.TestCase):
) )
def verify_update( def verify_update(
self, test_method, resource_type, base_path=None, *, self,
method_args=None, method_kwargs=None, test_method,
expected_args=None, expected_kwargs=None, expected_result="result", resource_type,
base_path=None,
*,
method_args=None,
method_kwargs=None,
expected_args=None,
expected_kwargs=None,
expected_result="result",
mock_method="openstack.proxy.Proxy._update", mock_method="openstack.proxy.Proxy._update",
): ):
if method_args is None: if method_args is None:
@ -259,7 +316,8 @@ class TestProxyBase(base.TestCase):
) )
def verify_wait_for_status( def verify_wait_for_status(
self, test_method, self,
test_method,
mock_method="openstack.resource.wait_for_status", mock_method="openstack.resource.wait_for_status",
**kwargs, **kwargs,
): ):

File diff suppressed because it is too large Load Diff

View File

@ -66,16 +66,17 @@ class StatsdFixture(fixtures.Fixture):
class TestStats(base.TestCase): class TestStats(base.TestCase):
def setUp(self): def setUp(self):
self.statsd = StatsdFixture() self.statsd = StatsdFixture()
self.useFixture(self.statsd) self.useFixture(self.statsd)
# note, use 127.0.0.1 rather than localhost to avoid getting ipv6 # note, use 127.0.0.1 rather than localhost to avoid getting ipv6
# see: https://github.com/jsocol/pystatsd/issues/61 # see: https://github.com/jsocol/pystatsd/issues/61
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable('STATSD_HOST', '127.0.0.1')) fixtures.EnvironmentVariable('STATSD_HOST', '127.0.0.1')
)
self.useFixture( self.useFixture(
fixtures.EnvironmentVariable('STATSD_PORT', str(self.statsd.port))) fixtures.EnvironmentVariable('STATSD_PORT', str(self.statsd.port))
)
self.add_info_on_exception('statsd_content', self.statsd.stats) self.add_info_on_exception('statsd_content', self.statsd.stats)
# Set up the above things before the super setup so that we have the # Set up the above things before the super setup so that we have the
@ -93,7 +94,8 @@ class TestStats(base.TestCase):
samples.append(s) samples.append(s)
self.addDetail( self.addDetail(
'prometheus_samples', 'prometheus_samples',
testtools.content.text_content(pprint.pformat(samples))) testtools.content.text_content(pprint.pformat(samples)),
)
def assert_reported_stat(self, key, value=None, kind=None): def assert_reported_stat(self, key, value=None, kind=None):
"""Check statsd output """Check statsd output
@ -127,7 +129,8 @@ class TestStats(base.TestCase):
# newlines; thus we first flatten the stats out into # newlines; thus we first flatten the stats out into
# single entries. # single entries.
stats = itertools.chain.from_iterable( stats = itertools.chain.from_iterable(
[s.decode('utf-8').split('\n') for s in self.statsd.stats]) [s.decode('utf-8').split('\n') for s in self.statsd.stats]
)
for stat in stats: for stat in stats:
k, v = stat.split(':') k, v = stat.split(':')
if key == k: if key == k:
@ -166,127 +169,184 @@ class TestStats(base.TestCase):
def test_list_projects(self): def test_list_projects(self):
mock_uri = self.get_mock_url( mock_uri = self.get_mock_url(
service_type='identity', resource='projects', service_type='identity', resource='projects', base_url_append='v3'
base_url_append='v3') )
self.register_uris([ self.register_uris(
dict(method='GET', uri=mock_uri, status_code=200, [
json={'projects': []})]) dict(
method='GET',
uri=mock_uri,
status_code=200,
json={'projects': []},
)
]
)
self.cloud.list_projects() self.cloud.list_projects()
self.assert_calls() self.assert_calls()
self.assert_reported_stat( self.assert_reported_stat(
'openstack.api.identity.GET.projects.200', value='1', kind='c') 'openstack.api.identity.GET.projects.200', value='1', kind='c'
)
self.assert_prometheus_stat( self.assert_prometheus_stat(
'openstack_http_requests_total', 1, dict( 'openstack_http_requests_total',
1,
dict(
service_type='identity', service_type='identity',
endpoint=mock_uri, endpoint=mock_uri,
method='GET', method='GET',
status_code='200')) status_code='200',
),
)
def test_projects(self): def test_projects(self):
mock_uri = self.get_mock_url( mock_uri = self.get_mock_url(
service_type='identity', resource='projects', service_type='identity', resource='projects', base_url_append='v3'
base_url_append='v3') )
self.register_uris([ self.register_uris(
dict(method='GET', uri=mock_uri, status_code=200, [
json={'projects': []})]) dict(
method='GET',
uri=mock_uri,
status_code=200,
json={'projects': []},
)
]
)
list(self.cloud.identity.projects()) list(self.cloud.identity.projects())
self.assert_calls() self.assert_calls()
self.assert_reported_stat( self.assert_reported_stat(
'openstack.api.identity.GET.projects.200', value='1', kind='c') 'openstack.api.identity.GET.projects.200', value='1', kind='c'
)
self.assert_prometheus_stat( self.assert_prometheus_stat(
'openstack_http_requests_total', 1, dict( 'openstack_http_requests_total',
1,
dict(
service_type='identity', service_type='identity',
endpoint=mock_uri, endpoint=mock_uri,
method='GET', method='GET',
status_code='200')) status_code='200',
),
)
def test_servers(self): def test_servers(self):
mock_uri = 'https://compute.example.com/v2.1/servers/detail' mock_uri = 'https://compute.example.com/v2.1/servers/detail'
self.register_uris([ self.register_uris(
self.get_nova_discovery_mock_dict(), [
dict(method='GET', uri=mock_uri, status_code=200, self.get_nova_discovery_mock_dict(),
json={'servers': []})]) dict(
method='GET',
uri=mock_uri,
status_code=200,
json={'servers': []},
),
]
)
list(self.cloud.compute.servers()) list(self.cloud.compute.servers())
self.assert_calls() self.assert_calls()
self.assert_reported_stat( self.assert_reported_stat(
'openstack.api.compute.GET.servers_detail.200', 'openstack.api.compute.GET.servers_detail.200', value='1', kind='c'
value='1', kind='c') )
self.assert_reported_stat( self.assert_reported_stat(
'openstack.api.compute.GET.servers_detail.200', 'openstack.api.compute.GET.servers_detail.200',
value='0', kind='ms') value='0',
kind='ms',
)
self.assert_prometheus_stat( self.assert_prometheus_stat(
'openstack_http_requests_total', 1, dict( 'openstack_http_requests_total',
1,
dict(
service_type='compute', service_type='compute',
endpoint=mock_uri, endpoint=mock_uri,
method='GET', method='GET',
status_code='200')) status_code='200',
),
)
def test_servers_no_detail(self): def test_servers_no_detail(self):
mock_uri = 'https://compute.example.com/v2.1/servers' mock_uri = 'https://compute.example.com/v2.1/servers'
self.register_uris([ self.register_uris(
dict(method='GET', uri=mock_uri, status_code=200, [
json={'servers': []})]) dict(
method='GET',
uri=mock_uri,
status_code=200,
json={'servers': []},
)
]
)
self.cloud.compute.get('/servers') self.cloud.compute.get('/servers')
self.assert_calls() self.assert_calls()
self.assert_reported_stat( self.assert_reported_stat(
'openstack.api.compute.GET.servers.200', value='1', kind='c') 'openstack.api.compute.GET.servers.200', value='1', kind='c'
)
self.assert_reported_stat( self.assert_reported_stat(
'openstack.api.compute.GET.servers.200', value='0', kind='ms') 'openstack.api.compute.GET.servers.200', value='0', kind='ms'
)
self.assert_reported_stat( self.assert_reported_stat(
'openstack.api.compute.GET.servers.attempted', value='1', kind='c') 'openstack.api.compute.GET.servers.attempted', value='1', kind='c'
)
self.assert_prometheus_stat( self.assert_prometheus_stat(
'openstack_http_requests_total', 1, dict( 'openstack_http_requests_total',
1,
dict(
service_type='compute', service_type='compute',
endpoint=mock_uri, endpoint=mock_uri,
method='GET', method='GET',
status_code='200')) status_code='200',
),
)
def test_servers_error(self): def test_servers_error(self):
mock_uri = 'https://compute.example.com/v2.1/servers' mock_uri = 'https://compute.example.com/v2.1/servers'
self.register_uris([ self.register_uris(
dict(method='GET', uri=mock_uri, status_code=500, [dict(method='GET', uri=mock_uri, status_code=500, json={})]
json={})]) )
self.cloud.compute.get('/servers') self.cloud.compute.get('/servers')
self.assert_calls() self.assert_calls()
self.assert_reported_stat( self.assert_reported_stat(
'openstack.api.compute.GET.servers.500', value='1', kind='c') 'openstack.api.compute.GET.servers.500', value='1', kind='c'
)
self.assert_reported_stat( self.assert_reported_stat(
'openstack.api.compute.GET.servers.500', value='0', kind='ms') 'openstack.api.compute.GET.servers.500', value='0', kind='ms'
)
self.assert_reported_stat( self.assert_reported_stat(
'openstack.api.compute.GET.servers.attempted', value='1', kind='c') 'openstack.api.compute.GET.servers.attempted', value='1', kind='c'
)
self.assert_prometheus_stat( self.assert_prometheus_stat(
'openstack_http_requests_total', 1, dict( 'openstack_http_requests_total',
1,
dict(
service_type='compute', service_type='compute',
endpoint=mock_uri, endpoint=mock_uri,
method='GET', method='GET',
status_code='500')) status_code='500',
),
)
def test_timeout(self): def test_timeout(self):
mock_uri = 'https://compute.example.com/v2.1/servers' mock_uri = 'https://compute.example.com/v2.1/servers'
self.register_uris([ self.register_uris(
dict(method='GET', uri=mock_uri, [dict(method='GET', uri=mock_uri, exc=rexceptions.ConnectTimeout)]
exc=rexceptions.ConnectTimeout) )
])
try: try:
self.cloud.compute.get('/servers') self.cloud.compute.get('/servers')
@ -294,13 +354,14 @@ class TestStats(base.TestCase):
pass pass
self.assert_reported_stat( self.assert_reported_stat(
'openstack.api.compute.GET.servers.failed', value='1', kind='c') 'openstack.api.compute.GET.servers.failed', value='1', kind='c'
)
self.assert_reported_stat( self.assert_reported_stat(
'openstack.api.compute.GET.servers.attempted', value='1', kind='c') 'openstack.api.compute.GET.servers.attempted', value='1', kind='c'
)
class TestNoStats(base.TestCase): class TestNoStats(base.TestCase):
def setUp(self): def setUp(self):
super(TestNoStats, self).setUp() super(TestNoStats, self).setUp()
self.statsd = StatsdFixture() self.statsd = StatsdFixture()
@ -309,12 +370,19 @@ class TestNoStats(base.TestCase):
def test_no_stats(self): def test_no_stats(self):
mock_uri = self.get_mock_url( mock_uri = self.get_mock_url(
service_type='identity', resource='projects', service_type='identity', resource='projects', base_url_append='v3'
base_url_append='v3') )
self.register_uris([ self.register_uris(
dict(method='GET', uri=mock_uri, status_code=200, [
json={'projects': []})]) dict(
method='GET',
uri=mock_uri,
status_code=200,
json={'projects': []},
)
]
)
self.cloud.identity._statsd_client = None self.cloud.identity._statsd_client = None
list(self.cloud.identity.projects()) list(self.cloud.identity.projects())

View File

@ -29,7 +29,6 @@ from openstack import utils
class Test_enable_logging(base.TestCase): class Test_enable_logging(base.TestCase):
def setUp(self): def setUp(self):
super(Test_enable_logging, self).setUp() super(Test_enable_logging, self).setUp()
self.openstack_logger = mock.Mock() self.openstack_logger = mock.Mock()
@ -54,10 +53,11 @@ class Test_enable_logging(base.TestCase):
self.stevedore_logger, self.stevedore_logger,
self.ksa_logger_1, self.ksa_logger_1,
self.ksa_logger_2, self.ksa_logger_2,
self.ksa_logger_3 self.ksa_logger_3,
] ]
self.useFixture( self.useFixture(
fixtures.MonkeyPatch('logging.getLogger', self.fake_get_logger)) fixtures.MonkeyPatch('logging.getLogger', self.fake_get_logger)
)
def _console_tests(self, level, debug, stream): def _console_tests(self, level, debug, stream):
@ -69,7 +69,8 @@ class Test_enable_logging(base.TestCase):
def _file_tests(self, level, debug): def _file_tests(self, level, debug):
file_handler = mock.Mock() file_handler = mock.Mock()
self.useFixture( self.useFixture(
fixtures.MonkeyPatch('logging.FileHandler', file_handler)) fixtures.MonkeyPatch('logging.FileHandler', file_handler)
)
fake_path = "fake/path.log" fake_path = "fake/path.log"
openstack.enable_logging(debug=debug, path=fake_path) openstack.enable_logging(debug=debug, path=fake_path)
@ -85,7 +86,8 @@ class Test_enable_logging(base.TestCase):
self.assertEqual(self.openstack_logger.addHandler.call_count, 1) self.assertEqual(self.openstack_logger.addHandler.call_count, 1)
self.assertIsInstance( self.assertIsInstance(
self.openstack_logger.addHandler.call_args_list[0][0][0], self.openstack_logger.addHandler.call_args_list[0][0][0],
logging.StreamHandler) logging.StreamHandler,
)
def test_debug_console_stderr(self): def test_debug_console_stderr(self):
self._console_tests(logging.DEBUG, True, sys.stderr) self._console_tests(logging.DEBUG, True, sys.stderr)
@ -107,7 +109,6 @@ class Test_enable_logging(base.TestCase):
class Test_urljoin(base.TestCase): class Test_urljoin(base.TestCase):
def test_strings(self): def test_strings(self):
root = "http://www.example.com" root = "http://www.example.com"
leaves = "foo", "bar" leaves = "foo", "bar"
@ -138,21 +139,20 @@ class TestSupportsMicroversion(base.TestCase):
def setUp(self): def setUp(self):
super(TestSupportsMicroversion, self).setUp() super(TestSupportsMicroversion, self).setUp()
self.adapter = mock.Mock(spec=['get_endpoint_data']) self.adapter = mock.Mock(spec=['get_endpoint_data'])
self.endpoint_data = mock.Mock(spec=['min_microversion', self.endpoint_data = mock.Mock(
'max_microversion'], spec=['min_microversion', 'max_microversion'],
min_microversion='1.1', min_microversion='1.1',
max_microversion='1.99') max_microversion='1.99',
)
self.adapter.get_endpoint_data.return_value = self.endpoint_data self.adapter.get_endpoint_data.return_value = self.endpoint_data
def test_requested_supported_no_default(self): def test_requested_supported_no_default(self):
self.adapter.default_microversion = None self.adapter.default_microversion = None
self.assertTrue( self.assertTrue(utils.supports_microversion(self.adapter, '1.2'))
utils.supports_microversion(self.adapter, '1.2'))
def test_requested_not_supported_no_default(self): def test_requested_not_supported_no_default(self):
self.adapter.default_microversion = None self.adapter.default_microversion = None
self.assertFalse( self.assertFalse(utils.supports_microversion(self.adapter, '2.2'))
utils.supports_microversion(self.adapter, '2.2'))
def test_requested_not_supported_no_default_exception(self): def test_requested_not_supported_no_default_exception(self):
self.adapter.default_microversion = None self.adapter.default_microversion = None
@ -161,22 +161,20 @@ class TestSupportsMicroversion(base.TestCase):
utils.supports_microversion, utils.supports_microversion,
self.adapter, self.adapter,
'2.2', '2.2',
True) True,
)
def test_requested_supported_higher_default(self): def test_requested_supported_higher_default(self):
self.adapter.default_microversion = '1.8' self.adapter.default_microversion = '1.8'
self.assertTrue( self.assertTrue(utils.supports_microversion(self.adapter, '1.6'))
utils.supports_microversion(self.adapter, '1.6'))
def test_requested_supported_equal_default(self): def test_requested_supported_equal_default(self):
self.adapter.default_microversion = '1.8' self.adapter.default_microversion = '1.8'
self.assertTrue( self.assertTrue(utils.supports_microversion(self.adapter, '1.8'))
utils.supports_microversion(self.adapter, '1.8'))
def test_requested_supported_lower_default(self): def test_requested_supported_lower_default(self):
self.adapter.default_microversion = '1.2' self.adapter.default_microversion = '1.2'
self.assertFalse( self.assertFalse(utils.supports_microversion(self.adapter, '1.8'))
utils.supports_microversion(self.adapter, '1.8'))
def test_requested_supported_lower_default_exception(self): def test_requested_supported_lower_default_exception(self):
self.adapter.default_microversion = '1.2' self.adapter.default_microversion = '1.2'
@ -185,54 +183,58 @@ class TestSupportsMicroversion(base.TestCase):
utils.supports_microversion, utils.supports_microversion,
self.adapter, self.adapter,
'1.8', '1.8',
True) True,
)
@mock.patch('openstack.utils.supports_microversion') @mock.patch('openstack.utils.supports_microversion')
def test_require_microversion(self, sm_mock): def test_require_microversion(self, sm_mock):
utils.require_microversion(self.adapter, '1.2') utils.require_microversion(self.adapter, '1.2')
sm_mock.assert_called_with(self.adapter, sm_mock.assert_called_with(self.adapter, '1.2', raise_exception=True)
'1.2',
raise_exception=True)
class TestMaximumSupportedMicroversion(base.TestCase): class TestMaximumSupportedMicroversion(base.TestCase):
def setUp(self): def setUp(self):
super(TestMaximumSupportedMicroversion, self).setUp() super(TestMaximumSupportedMicroversion, self).setUp()
self.adapter = mock.Mock(spec=['get_endpoint_data']) self.adapter = mock.Mock(spec=['get_endpoint_data'])
self.endpoint_data = mock.Mock(spec=['min_microversion', self.endpoint_data = mock.Mock(
'max_microversion'], spec=['min_microversion', 'max_microversion'],
min_microversion=None, min_microversion=None,
max_microversion='1.99') max_microversion='1.99',
)
self.adapter.get_endpoint_data.return_value = self.endpoint_data self.adapter.get_endpoint_data.return_value = self.endpoint_data
def test_with_none(self): def test_with_none(self):
self.assertIsNone(utils.maximum_supported_microversion(self.adapter, self.assertIsNone(
None)) utils.maximum_supported_microversion(self.adapter, None)
)
def test_with_value(self): def test_with_value(self):
self.assertEqual('1.42', self.assertEqual(
utils.maximum_supported_microversion(self.adapter, '1.42', utils.maximum_supported_microversion(self.adapter, '1.42')
'1.42')) )
def test_value_more_than_max(self): def test_value_more_than_max(self):
self.assertEqual('1.99', self.assertEqual(
utils.maximum_supported_microversion(self.adapter, '1.99', utils.maximum_supported_microversion(self.adapter, '1.100')
'1.100')) )
def test_value_less_than_min(self): def test_value_less_than_min(self):
self.endpoint_data.min_microversion = '1.42' self.endpoint_data.min_microversion = '1.42'
self.assertIsNone(utils.maximum_supported_microversion(self.adapter, self.assertIsNone(
'1.2')) utils.maximum_supported_microversion(self.adapter, '1.2')
)
class TestOsServiceTypesVersion(base.TestCase): class TestOsServiceTypesVersion(base.TestCase):
def test_ost_version(self): def test_ost_version(self):
ost_version = '2019-05-01T19:53:21.498745' ost_version = '2019-05-01T19:53:21.498745'
self.assertEqual( self.assertEqual(
ost_version, os_service_types.ServiceTypes().version, ost_version,
os_service_types.ServiceTypes().version,
"This project must be pinned to the latest version of " "This project must be pinned to the latest version of "
"os-service-types. Please bump requirements.txt and " "os-service-types. Please bump requirements.txt and "
"lower-constraints.txt accordingly.") "lower-constraints.txt accordingly.",
)
class TestTinyDAG(base.TestCase): class TestTinyDAG(base.TestCase):
@ -243,7 +245,7 @@ class TestTinyDAG(base.TestCase):
'd': ['e'], 'd': ['e'],
'e': [], 'e': [],
'f': ['e'], 'f': ['e'],
'g': ['e'] 'g': ['e'],
} }
def _verify_order(self, test_graph, test_list): def _verify_order(self, test_graph, test_list):
@ -306,13 +308,13 @@ def test_walker_fn(graph, node, lst):
class Test_md5(base.TestCase): class Test_md5(base.TestCase):
def setUp(self): def setUp(self):
super(Test_md5, self).setUp() super(Test_md5, self).setUp()
self.md5_test_data = "Openstack forever".encode('utf-8') self.md5_test_data = "Openstack forever".encode('utf-8')
try: try:
self.md5_digest = hashlib.md5( # nosec self.md5_digest = hashlib.md5( # nosec
self.md5_test_data).hexdigest() self.md5_test_data
).hexdigest()
self.fips_enabled = False self.fips_enabled = False
except ValueError: except ValueError:
self.md5_digest = '0d6dc3c588ae71a04ce9a6beebbbba06' self.md5_digest = '0d6dc3c588ae71a04ce9a6beebbbba06'
@ -327,15 +329,17 @@ class Test_md5(base.TestCase):
# [digital envelope routines: EVP_DigestInit_ex] disabled for FIPS # [digital envelope routines: EVP_DigestInit_ex] disabled for FIPS
self.assertRaises(ValueError, utils.md5, self.md5_test_data) self.assertRaises(ValueError, utils.md5, self.md5_test_data)
if not self.fips_enabled: if not self.fips_enabled:
digest = utils.md5(self.md5_test_data, digest = utils.md5(
usedforsecurity=True).hexdigest() self.md5_test_data, usedforsecurity=True
).hexdigest()
self.assertEqual(digest, self.md5_digest) self.assertEqual(digest, self.md5_digest)
else: else:
self.assertRaises( self.assertRaises(
ValueError, utils.md5, self.md5_test_data, ValueError, utils.md5, self.md5_test_data, usedforsecurity=True
usedforsecurity=True) )
digest = utils.md5(self.md5_test_data, digest = utils.md5(
usedforsecurity=False).hexdigest() self.md5_test_data, usedforsecurity=False
).hexdigest()
self.assertEqual(digest, self.md5_digest) self.assertEqual(digest, self.md5_digest)
def test_md5_without_data(self): def test_md5_without_data(self):
@ -363,25 +367,25 @@ class Test_md5(base.TestCase):
self.assertRaises(TypeError, hashlib.md5, u'foo') self.assertRaises(TypeError, hashlib.md5, u'foo')
self.assertRaises(TypeError, utils.md5, u'foo') self.assertRaises(TypeError, utils.md5, u'foo')
self.assertRaises( self.assertRaises(
TypeError, utils.md5, u'foo', usedforsecurity=True) TypeError, utils.md5, u'foo', usedforsecurity=True
)
else: else:
self.assertRaises(ValueError, hashlib.md5, u'foo') self.assertRaises(ValueError, hashlib.md5, u'foo')
self.assertRaises(ValueError, utils.md5, u'foo') self.assertRaises(ValueError, utils.md5, u'foo')
self.assertRaises( self.assertRaises(
ValueError, utils.md5, u'foo', usedforsecurity=True) ValueError, utils.md5, u'foo', usedforsecurity=True
self.assertRaises( )
TypeError, utils.md5, u'foo', usedforsecurity=False) self.assertRaises(TypeError, utils.md5, u'foo', usedforsecurity=False)
def test_none_data_raises_type_error(self): def test_none_data_raises_type_error(self):
if not self.fips_enabled: if not self.fips_enabled:
self.assertRaises(TypeError, hashlib.md5, None) self.assertRaises(TypeError, hashlib.md5, None)
self.assertRaises(TypeError, utils.md5, None) self.assertRaises(TypeError, utils.md5, None)
self.assertRaises( self.assertRaises(TypeError, utils.md5, None, usedforsecurity=True)
TypeError, utils.md5, None, usedforsecurity=True)
else: else:
self.assertRaises(ValueError, hashlib.md5, None) self.assertRaises(ValueError, hashlib.md5, None)
self.assertRaises(ValueError, utils.md5, None) self.assertRaises(ValueError, utils.md5, None)
self.assertRaises( self.assertRaises(
ValueError, utils.md5, None, usedforsecurity=True) ValueError, utils.md5, None, usedforsecurity=True
self.assertRaises( )
TypeError, utils.md5, None, usedforsecurity=False) self.assertRaises(TypeError, utils.md5, None, usedforsecurity=False)

View File

@ -57,7 +57,8 @@ def iterate_timeout(timeout, message, wait=2):
except ValueError: except ValueError:
raise exceptions.SDKException( raise exceptions.SDKException(
"Wait value must be an int or float value. {wait} given" "Wait value must be an int or float value. {wait} given"
" instead".format(wait=wait)) " instead".format(wait=wait)
)
start = time.time() start = time.time()
count = 0 count = 0
@ -76,6 +77,7 @@ def get_string_format_keys(fmt_string, old_style=True):
use the old style string formatting. use the old style string formatting.
""" """
if old_style: if old_style:
class AccessSaver: class AccessSaver:
def __init__(self): def __init__(self):
self.keys = [] self.keys = []
@ -115,25 +117,29 @@ def supports_microversion(adapter, microversion, raise_exception=False):
""" """
endpoint_data = adapter.get_endpoint_data() endpoint_data = adapter.get_endpoint_data()
if (endpoint_data.min_microversion if (
and endpoint_data.max_microversion endpoint_data.min_microversion
and discover.version_between( and endpoint_data.max_microversion
endpoint_data.min_microversion, and discover.version_between(
endpoint_data.max_microversion, endpoint_data.min_microversion,
microversion)): endpoint_data.max_microversion,
microversion,
)
):
if adapter.default_microversion is not None: if adapter.default_microversion is not None:
# If default_microversion is set - evaluate # If default_microversion is set - evaluate
# whether it match the expectation # whether it match the expectation
candidate = discover.normalize_version_number( candidate = discover.normalize_version_number(
adapter.default_microversion) adapter.default_microversion
)
required = discover.normalize_version_number(microversion) required = discover.normalize_version_number(microversion)
supports = discover.version_match(required, candidate) supports = discover.version_match(required, candidate)
if raise_exception and not supports: if raise_exception and not supports:
raise exceptions.SDKException( raise exceptions.SDKException(
'Required microversion {ver} is higher than currently ' 'Required microversion {ver} is higher than currently '
'selected {curr}'.format( 'selected {curr}'.format(
ver=microversion, ver=microversion, curr=adapter.default_microversion
curr=adapter.default_microversion) )
) )
return supports return supports
return True return True
@ -175,19 +181,24 @@ def pick_microversion(session, required):
if session.default_microversion is not None: if session.default_microversion is not None:
default = discover.normalize_version_number( default = discover.normalize_version_number(
session.default_microversion) session.default_microversion
)
if required is None: if required is None:
required = default required = default
else: else:
required = (default if discover.version_match(required, default) required = (
else required) default
if discover.version_match(required, default)
else required
)
if required is not None: if required is not None:
if not supports_microversion(session, required): if not supports_microversion(session, required):
raise exceptions.SDKException( raise exceptions.SDKException(
'Requested microversion is not supported by the server side ' 'Requested microversion is not supported by the server side '
'or the default microversion is too low') 'or the default microversion is too low'
)
return discover.version_to_string(required) return discover.version_to_string(required)
@ -212,8 +223,10 @@ def maximum_supported_microversion(adapter, client_maximum):
if endpoint_data is None: if endpoint_data is None:
log = _log.setup_logging('openstack') log = _log.setup_logging('openstack')
log.warning('Cannot determine endpoint data for service %s', log.warning(
adapter.service_type or adapter.service_name) 'Cannot determine endpoint data for service %s',
adapter.service_type or adapter.service_name,
)
return None return None
if not endpoint_data.max_microversion: if not endpoint_data.max_microversion:
@ -221,11 +234,13 @@ def maximum_supported_microversion(adapter, client_maximum):
client_max = discover.normalize_version_number(client_maximum) client_max = discover.normalize_version_number(client_maximum)
server_max = discover.normalize_version_number( server_max = discover.normalize_version_number(
endpoint_data.max_microversion) endpoint_data.max_microversion
)
if endpoint_data.min_microversion: if endpoint_data.min_microversion:
server_min = discover.normalize_version_number( server_min = discover.normalize_version_number(
endpoint_data.min_microversion) endpoint_data.min_microversion
)
if client_max < server_min: if client_max < server_min:
# NOTE(dtantsur): we may want to raise in this case, but this keeps # NOTE(dtantsur): we may want to raise in this case, but this keeps
# the current behavior intact. # the current behavior intact.
@ -265,6 +280,7 @@ try:
# See https://docs.python.org/3.9/library/hashlib.html # See https://docs.python.org/3.9/library/hashlib.html
md5 = hashlib.md5 md5 = hashlib.md5
except TypeError: except TypeError:
def md5(string=b'', usedforsecurity=True): def md5(string=b'', usedforsecurity=True):
"""Return an md5 hashlib object without usedforsecurity parameter """Return an md5 hashlib object without usedforsecurity parameter
For python distributions that do not yet support this keyword For python distributions that do not yet support this keyword
@ -314,8 +330,7 @@ class TinyDAG:
@property @property
def graph(self): def graph(self):
"""Get graph as adjacency dict """Get graph as adjacency dict"""
"""
return self._graph return self._graph
def add_node(self, node): def add_node(self, node):
@ -332,8 +347,7 @@ class TinyDAG:
self.add_edge(k, dep) self.add_edge(k, dep)
def walk(self, timeout=None): def walk(self, timeout=None):
"""Start the walking from the beginning. """Start the walking from the beginning."""
"""
if timeout: if timeout:
self._wait_timeout = timeout self._wait_timeout = timeout
return self return self
@ -345,17 +359,16 @@ class TinyDAG:
def __next__(self): def __next__(self):
# Start waiting if it is expected to get something # Start waiting if it is expected to get something
# (counting down from graph length to 0). # (counting down from graph length to 0).
if (self._it_cnt > 0): if self._it_cnt > 0:
self._it_cnt -= 1 self._it_cnt -= 1
try: try:
res = self._queue.get( res = self._queue.get(block=True, timeout=self._wait_timeout)
block=True,
timeout=self._wait_timeout)
return res return res
except queue.Empty: except queue.Empty:
raise exceptions.SDKException('Timeout waiting for ' raise exceptions.SDKException(
'cleanup task to complete') 'Timeout waiting for ' 'cleanup task to complete'
)
else: else:
raise StopIteration raise StopIteration
@ -410,13 +423,13 @@ class TinyDAG:
# it we can have a reduced version. # it we can have a reduced version.
class Munch(dict): class Munch(dict):
"""A slightly stripped version of munch.Munch class""" """A slightly stripped version of munch.Munch class"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.update(*args, **kwargs) self.update(*args, **kwargs)
# only called if k not found in normal places # only called if k not found in normal places
def __getattr__(self, k): def __getattr__(self, k):
"""Gets key if it exists, otherwise throws AttributeError. """Gets key if it exists, otherwise throws AttributeError."""
"""
try: try:
return object.__getattribute__(self, k) return object.__getattribute__(self, k)
except AttributeError: except AttributeError:
@ -427,8 +440,8 @@ class Munch(dict):
def __setattr__(self, k, v): def __setattr__(self, k, v):
"""Sets attribute k if it exists, otherwise sets key k. A KeyError """Sets attribute k if it exists, otherwise sets key k. A KeyError
raised by set-item (only likely if you subclass Munch) will raised by set-item (only likely if you subclass Munch) will
propagate as an AttributeError instead. propagate as an AttributeError instead.
""" """
try: try:
# Throws exception if not in prototype chain # Throws exception if not in prototype chain
@ -459,8 +472,7 @@ class Munch(dict):
object.__delattr__(self, k) object.__delattr__(self, k)
def toDict(self): def toDict(self):
"""Recursively converts a munch back into a dictionary. """Recursively converts a munch back into a dictionary."""
"""
return unmunchify(self) return unmunchify(self)
@property @property
@ -468,7 +480,7 @@ class Munch(dict):
return self.toDict() return self.toDict()
def __repr__(self): def __repr__(self):
"""Invertible* string-form of a Munch. """ """Invertible* string-form of a Munch."""
return f'{self.__class__.__name__}({dict.__repr__(self)})' return f'{self.__class__.__name__}({dict.__repr__(self)})'
def __dir__(self): def __dir__(self):

View File

@ -197,9 +197,13 @@ htmlhelp_basename = 'shadeReleaseNotesdoc'
# (source start file, target name, title, # (source start file, target name, title,
# author, documentclass [howto, manual, or own class]). # author, documentclass [howto, manual, or own class]).
latex_documents = [ latex_documents = [
('index', 'shadeReleaseNotes.tex', (
'Shade Release Notes Documentation', 'index',
'Shade Developers', 'manual'), 'shadeReleaseNotes.tex',
'Shade Release Notes Documentation',
'Shade Developers',
'manual',
),
] ]
# The name of an image file (relative to this directory) to place at the top of # The name of an image file (relative to this directory) to place at the top of
@ -228,9 +232,13 @@ latex_documents = [
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
man_pages = [ man_pages = [
('index', 'shadereleasenotes', (
'shade Release Notes Documentation', 'index',
['shade Developers'], 1) 'shadereleasenotes',
'shade Release Notes Documentation',
['shade Developers'],
1,
)
] ]
# If true, show URL addresses after external links. # If true, show URL addresses after external links.
@ -243,11 +251,15 @@ man_pages = [
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ texinfo_documents = [
('index', 'shadeReleaseNotes', (
'shade Release Notes Documentation', 'index',
'shade Developers', 'shadeReleaseNotes', 'shadeReleaseNotes',
'A client library for interacting with OpenStack clouds', 'shade Release Notes Documentation',
'Miscellaneous'), 'shade Developers',
'shadeReleaseNotes',
'A client library for interacting with OpenStack clouds',
'Miscellaneous',
),
] ]
# Documents to append as an appendix to all manuals. # Documents to append as an appendix to all manuals.

View File

@ -16,6 +16,4 @@
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
import setuptools import setuptools
setuptools.setup( setuptools.setup(setup_requires=['pbr>=2.0.0'], pbr=True)
setup_requires=['pbr>=2.0.0'],
pbr=True)

View File

@ -36,8 +36,9 @@ def print_version(version):
if version['status'] in ('CURRENT', 'stable'): if version['status'] in ('CURRENT', 'stable'):
print( print(
"\tVersion ID: {id} updated {updated}".format( "\tVersion ID: {id} updated {updated}".format(
id=version.get('id'), id=version.get('id'), updated=version.get('updated')
updated=version.get('updated'))) )
)
verbose = '-v' in sys.argv verbose = '-v' in sys.argv
@ -71,7 +72,8 @@ for cloud in openstack.config.OpenStackConfig().get_all_clouds():
if port: if port:
stripped = '{stripped}:{port}'.format(stripped=stripped, port=port) stripped = '{stripped}:{port}'.format(stripped=stripped, port=port)
endpoint = urlparse.urlunsplit( endpoint = urlparse.urlunsplit(
(url.scheme, url.netloc, stripped, url.params, url.query)) (url.scheme, url.netloc, stripped, url.params, url.query)
)
print(" also {endpoint}".format(endpoint=endpoint)) print(" also {endpoint}".format(endpoint=endpoint))
try: try:
r = c.get(endpoint).json() r = c.get(endpoint).json()

View File

@ -35,22 +35,27 @@ for cloud in openstack.config.OpenStackConfig().get_all_clouds():
have_current = True have_current = True
print( print(
"\tVersion ID: {id} updated {updated}".format( "\tVersion ID: {id} updated {updated}".format(
id=version.get('id'), id=version.get('id'), updated=version.get('updated')
updated=version.get('updated'))) )
)
print("\tVersion Max: {max}".format(max=version.get('version')))
print( print(
"\tVersion Max: {max}".format(max=version.get('version'))) "\tVersion Min: {min}".format(min=version.get('min_version'))
print( )
"\tVersion Min: {min}".format(min=version.get('min_version')))
if not have_current: if not have_current:
for version in r['versions']: for version in r['versions']:
if version['status'] == 'SUPPORTED': if version['status'] == 'SUPPORTED':
have_current = True have_current = True
print( print(
"\tVersion ID: {id} updated {updated}".format( "\tVersion ID: {id} updated {updated}".format(
id=version.get('id'), id=version.get('id'), updated=version.get('updated')
updated=version.get('updated'))) )
)
print( print(
"\tVersion Max: {max}".format(max=version.get('version'))) "\tVersion Max: {max}".format(max=version.get('version'))
)
print( print(
"\tVersion Min: {min}".format( "\tVersion Min: {min}".format(
min=version.get('min_version'))) min=version.get('min_version')
)
)

View File

@ -42,16 +42,16 @@ def make_names():
if desc_class.__module__ != 'openstack.service_description': if desc_class.__module__ != 'openstack.service_description':
base_mod, dm = desc_class.__module__.rsplit('.', 1) base_mod, dm = desc_class.__module__.rsplit('.', 1)
imports.append( imports.append(
'from {base_mod} import {dm}'.format( 'from {base_mod} import {dm}'.format(base_mod=base_mod, dm=dm)
base_mod=base_mod, )
dm=dm))
else: else:
dm = 'service_description' dm = 'service_description'
dc = desc_class.__name__ dc = desc_class.__name__
services.append( services.append(
"{st} = {dm}.{dc}(service_type='{service_type}')".format( "{st} = {dm}.{dc}(service_type='{service_type}')".format(
st=st, dm=dm, dc=dc, service_type=service_type), st=st, dm=dm, dc=dc, service_type=service_type
),
) )
# Register the descriptor class with every known alias. Don't # Register the descriptor class with every known alias. Don't
@ -63,9 +63,8 @@ def make_names():
if alias_name[-1].isdigit(): if alias_name[-1].isdigit():
continue continue
services.append( services.append(
'{alias_name} = {st}'.format( '{alias_name} = {st}'.format(alias_name=alias_name, st=st)
alias_name=alias_name, )
st=st))
services.append('') services.append('')
print("# Generated file, to change, run tools/print-services.py") print("# Generated file, to change, run tools/print-services.py")
for imp in sorted(imports): for imp in sorted(imports):