Addapted flame to openstacksdk and shade.

Flame needed pemanent adjustments to mathe the changes in the
python-openstackclients. We now use openstacksdk or shade which will
handle themselves the compatibility.

We also made flame modular so that any-one can add features by
implementing there own flame managers and adding their modules to
the `openstack_flame` entry point.

This new flame version is also fully compatible with python 3.

Change-Id: I586a165b5022031963f504874bd50e1b11fe0d27
This commit is contained in:
Yves-Gwenael Bourhis 2018-11-21 16:06:48 +01:00
parent 2cc573d049
commit e798119841
66 changed files with 7292 additions and 4759 deletions

4
.gitignore vendored
View File

@ -1,4 +1,5 @@
*.py[cod]
.venv
# C extensions
*.so
@ -23,6 +24,7 @@ pip-log.txt
# Unit test / coverage reports
.coverage
cover
.tox
nosetests.xml
.testrepository
@ -49,3 +51,5 @@ ChangeLog
# Editors
*~
.*.swp
.idea
.vscode

28
CONTRIBUTING.rst Normal file
View File

@ -0,0 +1,28 @@
When developping on flameclient, do not forget to check code quality with the
`./checkcode` command, and then check you did not brake anything by running
`python -m unittest discover -v`.
To create flame modules you need to create a module with a class which
inheritates from `flameclient.resources.ResourceManager`
You need to implement the `api_resources` property and the `get_hot_resources()`
method (read their docstring for more information).
If you want you can also implement the `add_arguments(parser=None)` method to
add your own module's command line arguments.
You can also implement the `post_process()`,
`post_process_hot_resources(resources)`, `post_process_heat_template(template)` and/or
`post_process_adoption_data(adoption_data)` methods to perform
post processing after the generator's `extract_data` method was called
(read their docstring for more information). This allows you to modify results
before rendering the template.
These post processing methods are not threaded and are executed in order of the
managers' `post_priority` attribute (defaults to 100).
Then, you need to add in your package's `setup.py` or `setup.cfg` an
'openstack_flame' entry point pointing to the module file where your subclass
of `flameclient.resources.ResourceManager` is defined.
Once your package installed, flame will automatically discover all
'openstack_flame' entry points to load the corresponding modules, and all
loaded modules having a `flameclient.resources.ResourceManager` subclass will
have this subclass detected and added tho the list of managers.

21
LICENSE
View File

@ -1,21 +0,0 @@
This software is released under the MIT License.
Copyright (c) 2014 Cloudwatt
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
LICENSE Symbolic link
View File

@ -0,0 +1 @@
flameclient/LICENSE.txt

View File

@ -32,64 +32,154 @@ Then just run:
Usage
-----
usage: flame [-h] [--username USERNAME] [--password PASSWORD]
[--project PROJECT] [--region REGION] [--auth_url AUTH_URL]
[--os-auth-token OS_AUTH_TOKEN] [--insecure]
[--os-cert <certification>] [--os-key <key>]
[--endpoint_type ENDPOINT_TYPE] [--exclude-servers]
[--exclude-volumes] [--exclude-keypairs] [--generate-stack-data]
[--extract-ports] [--exclude-secgroup]
To use the CLI of flame::
usage: flame [-h] [--debug] [--generate-stack-data] [--include-constraints]
[--no-threads] [--prefetch] [--exclude-keypairs]
[--extract-ports] [--exclude-secgroups] [--exclude-servers]
[--exclude-volumes] [--os-cloud <name>] [--os-auth-type <name>]
[--os-auth-url OS_AUTH_URL] [--os-system-scope OS_SYSTEM_SCOPE]
[--os-domain-id OS_DOMAIN_ID] [--os-domain-name OS_DOMAIN_NAME]
[--os-project-id OS_PROJECT_ID]
[--os-project-name OS_PROJECT_NAME]
[--os-project-domain-id OS_PROJECT_DOMAIN_ID]
[--os-project-domain-name OS_PROJECT_DOMAIN_NAME]
[--os-trust-id OS_TRUST_ID]
[--os-default-domain-id OS_DEFAULT_DOMAIN_ID]
[--os-default-domain-name OS_DEFAULT_DOMAIN_NAME]
[--os-user-id OS_USER_ID] [--os-username OS_USERNAME]
[--os-user-domain-id OS_USER_DOMAIN_ID]
[--os-user-domain-name OS_USER_DOMAIN_NAME]
[--os-password OS_PASSWORD] [--insecure]
[--os-cacert <ca-certificate>] [--os-cert <certificate>]
[--os-key <key>] [--timeout <seconds>] [--collect-timing]
[--os-service-type <name>] [--os-service-name <name>]
[--os-interface <name>] [--os-region-name <name>]
[--os-endpoint-override <name>] [--os-api-version <name>]
Heat template and data file generator
optional arguments:
-h, --help show this help message and exit
--username USERNAME A user name with access to the project. Defaults to
env[OS_USERNAME]
--password PASSWORD The user's password. Defaults to env[OS_PASSWORD]
--project PROJECT Name of project. Defaults to env[OS_TENANT_NAME]
--region REGION Name of region. Defaults to env[OS_REGION_NAME]
--auth_url AUTH_URL Authentication URL. Defaults to env[OS_AUTH_URL].
--os-auth-token OS_AUTH_TOKEN
User's auth token. Defaults to env[OS_AUTH_TOKEN].
--os-cert <certificate>
Path to user's certificate needed to establish
two-way SSL connection with the identity service.
Defaults to env[OS_CERT].
--os-key <key> Path to the user's certificate private key.
Defaults to env[OS_KEY].
--insecure Explicitly allow clients to perform"insecure" SSL
(https) requests. The server's certificate will not be
verified against any certificate authorities. This
option should be used with caution.
--endpoint_type ENDPOINT_TYPE
Defaults to env[OS_ENDPOINT_TYPE] or publicURL
--exclude-servers Do not export in template server resources
--exclude-volumes Do not export in template volume resources
--exclude-keypairs Do not export in template key pair resources
--debug set debuging log level
--generate-stack-data
In addition to template, generate Heat stack data
file.
--include-constraints
Export in template custom constraints
--no-threads Deactivate threads for api calls, (usefull for (i)pdb
debugging.
--prefetch Prefetch all API calls (works only without --no-
threads
--exclude-keypairs Do not export in template key pair resources
--extract-ports Export the tenant network ports
--exclude-secgroups Do not export in template security group resources
--exclude-servers Do not export in template server resources
--exclude-volumes Do not export in template volume resources
--os-cloud <name> Named cloud to connect to
--os-auth-type <name>, --os-auth-plugin <name>
Authentication type to use
Authentication Options:
Options specific to the password plugin.
--os-auth-url OS_AUTH_URL
Authentication URL
--os-system-scope OS_SYSTEM_SCOPE
Scope for system operations
--os-domain-id OS_DOMAIN_ID
Domain ID to scope to
--os-domain-name OS_DOMAIN_NAME
Domain name to scope to
--os-project-id OS_PROJECT_ID, --os-tenant-id OS_PROJECT_ID
Project ID to scope to
--os-project-name OS_PROJECT_NAME, --os-tenant-name OS_PROJECT_NAME
Project name to scope to
--os-project-domain-id OS_PROJECT_DOMAIN_ID
Domain ID containing project
--os-project-domain-name OS_PROJECT_DOMAIN_NAME
Domain name containing project
--os-trust-id OS_TRUST_ID
Trust ID
--os-default-domain-id OS_DEFAULT_DOMAIN_ID
Optional domain ID to use with v3 and v2 parameters.
It will be used for both the user and project domain
in v3 and ignored in v2 authentication.
--os-default-domain-name OS_DEFAULT_DOMAIN_NAME
Optional domain name to use with v3 API and v2
parameters. It will be used for both the user and
project domain in v3 and ignored in v2 authentication.
--os-user-id OS_USER_ID
User id
--os-username OS_USERNAME, --os-user-name OS_USERNAME
Username
--os-user-domain-id OS_USER_DOMAIN_ID
User's domain id
--os-user-domain-name OS_USER_DOMAIN_NAME
User's domain name
--os-password OS_PASSWORD
User's password
API Connection Options:
Options controlling the HTTP API Connections
--insecure Explicitly allow client to perform "insecure" TLS
(https) requests. The server's certificate will not be
verified against any certificate authorities. This
option should be used with caution.
--os-cacert <ca-certificate>
Specify a CA bundle file to use in verifying a TLS
(https) server certificate. Defaults to
env[OS_CACERT].
--os-cert <certificate>
Defaults to env[OS_CERT].
--os-key <key> Defaults to env[OS_KEY].
--timeout <seconds> Set request timeout (in seconds).
--collect-timing Collect per-API call timing information.
Service Options:
Options controlling the specialization of the API Connection from
information found in the catalog
--os-service-type <name>
Service type to request from the catalog
--os-service-name <name>
Service name to request from the catalog
--os-interface <name>
API Interface to use [public, internal, admin]
--os-region-name <name>
Region of the cloud to use
--os-endpoint-override <name>
Endpoint to use instead of the endpoint in the catalog
--os-api-version <name>
Which version of the service API to use
Usage example
-------------
To use Flame you can provide yours OpenStack credentials as arguments :
$ flame --username user --password password --project project
--auth_url http://<Keystone_host>:5000/v2.0
To use Flame you can provide yours OpenStack credentials as arguments ::
$ flame --os-username 'user_name' \
--os-password 'password' \
--os-project-name 'project_name' \
--os-auth-url 'http://<Keystone_host>:5000/v2.0'
Or you can source your OpenStack RC file and use Flame without arguments.
To establish a two-way SSL connection with the identity service :
To establish a two-way SSL connection with the identity service ::
$flame --username arezmerita --os-auth-token keystonetoken \
--project project-arezmerita --auth_url http://<Keystone_host>:5000/v2.0
--os-cert <path/to/certificate> --os-key <path/to/key>
$flame --os-username 'user_name' \
--os-password 'password' \
--os-project-name 'project_name' \
--os-auth_url http://<Keystone_host>:5000/v2.0 \
--os-cert <path/to/certificate> \
--os-key <path/to/key>
Flame can be used with either a login and password pair or a keystone
token by exporting the OS_AUTH_TOKEN variable (the token is obtained
with keystone token-get).
token by exporting the OS_AUTH_TOKEN variable and the `--os-auth-type 'token'`
parameter (the token is obtained with keystone token-get )::
$ flame --os-auth-type 'token' \
--os-token 'token_id' \
--os-auth-url 'http://<Keystone_host>:5000/v2.0'

12
check_code Executable file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env bash
CURDIR=$(pwd)
SELFDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
cd $SELFDIR
python -m flake8 flameclient
if [[ $1 = '-v' ]]; then
python -m pylint --rcfile=pylintrc --reports=yes flameclient
else
python -m pylint --rcfile=pylintrc flameclient
fi
cd $CURDIR

4
dev-requirements.txt Normal file
View File

@ -0,0 +1,4 @@
bpython==0.17.1
ipython==5.8.0
ipdb==0.11
q==2.6

View File

@ -10,3 +10,4 @@ Or, if you have virtualenvwrapper installed::
$ mkvirtualenv python-flameclient
$ pip install python-flameclient

View File

@ -14,5 +14,3 @@ free IP addresses of a pool, for example, with a pool starting at 10.0.0.2 :
When this stack is imported in Heat, the DHCP server IP is set to the lowest
free IP address of its pool. Depending on the VM creation order, the DHCP
address can either collide with vm1's or vm2's IP.

View File

@ -8,45 +8,125 @@ To use install flame in a project::
To use the CLI of flame::
usage: flame [-h] [--username USERNAME] [--password PASSWORD]
[--project PROJECT] [--region REGION] [--auth_url AUTH_URL]
[--os-auth-token OS_AUTH_TOKEN] [--insecure]
[--os-cert <certification>] [--os-key <key>]
[--endpoint_type ENDPOINT_TYPE] [--exclude-servers]
[--exclude-volumes] [--exclude-keypairs] [--generate-stack-data]
[--extract-ports]
usage: flame [-h] [--debug] [--generate-stack-data] [--include-constraints]
[--no-threads] [--prefetch] [--exclude-keypairs]
[--extract-ports] [--exclude-secgroups] [--exclude-servers]
[--exclude-volumes] [--os-cloud <name>] [--os-auth-type <name>]
[--os-auth-url OS_AUTH_URL] [--os-system-scope OS_SYSTEM_SCOPE]
[--os-domain-id OS_DOMAIN_ID] [--os-domain-name OS_DOMAIN_NAME]
[--os-project-id OS_PROJECT_ID]
[--os-project-name OS_PROJECT_NAME]
[--os-project-domain-id OS_PROJECT_DOMAIN_ID]
[--os-project-domain-name OS_PROJECT_DOMAIN_NAME]
[--os-trust-id OS_TRUST_ID]
[--os-default-domain-id OS_DEFAULT_DOMAIN_ID]
[--os-default-domain-name OS_DEFAULT_DOMAIN_NAME]
[--os-user-id OS_USER_ID] [--os-username OS_USERNAME]
[--os-user-domain-id OS_USER_DOMAIN_ID]
[--os-user-domain-name OS_USER_DOMAIN_NAME]
[--os-password OS_PASSWORD] [--insecure]
[--os-cacert <ca-certificate>] [--os-cert <certificate>]
[--os-key <key>] [--timeout <seconds>] [--collect-timing]
[--os-service-type <name>] [--os-service-name <name>]
[--os-interface <name>] [--os-region-name <name>]
[--os-endpoint-override <name>] [--os-api-version <name>]
Heat template and data file generator
optional arguments:
-h, --help show this help message and exit
--username USERNAME A user name with access to the project. Defaults to
env[OS_USERNAME]
--password PASSWORD The user's password. Defaults to env[OS_PASSWORD]
--project PROJECT Name of project. Defaults to env[OS_TENANT_NAME]
--region REGION Name of region. Defaults to env[OS_REGION_NAME]
--auth_url AUTH_URL Authentication URL. Defaults to env[OS_AUTH_URL].
--os-auth-token OS_AUTH_TOKEN
User's auth token. Defaults to env[OS_AUTH_TOKEN].
--os-cert <certificate>
Path to user's certificate needed to establish
two-way SSL connection with the identity service.
Defaults to env[OS_CERT].
--os-key <key> Path to the user's certificate private key.
Defaults to env[OS_KEY].
--insecure Explicitly allow clients to perform"insecure" SSL
(https) requests. The server's certificate will not be
verified against any certificate authorities. This
option should be used with caution.
--endpoint_type ENDPOINT_TYPE
Defaults to env[OS_ENDPOINT_TYPE] or publicURL
--exclude-servers Do not export in template server resources
--exclude-volumes Do not export in template volume resources
--exclude-keypairs Do not export in template key pair resources
--debug set debuging log level
--generate-stack-data
In addition to template, generate Heat stack data
file.
--include-constraints
Export in template custom constraints
--no-threads Deactivate threads for api calls, (usefull for (i)pdb
debugging.
--prefetch Prefetch all API calls (works only without --no-
threads
--exclude-keypairs Do not export in template key pair resources
--extract-ports Export the tenant network ports
--exclude-secgroups Do not export in template security group resources
--exclude-servers Do not export in template server resources
--exclude-volumes Do not export in template volume resources
--os-cloud <name> Named cloud to connect to
--os-auth-type <name>, --os-auth-plugin <name>
Authentication type to use
Authentication Options:
Options specific to the password plugin.
--os-auth-url OS_AUTH_URL
Authentication URL
--os-system-scope OS_SYSTEM_SCOPE
Scope for system operations
--os-domain-id OS_DOMAIN_ID
Domain ID to scope to
--os-domain-name OS_DOMAIN_NAME
Domain name to scope to
--os-project-id OS_PROJECT_ID, --os-tenant-id OS_PROJECT_ID
Project ID to scope to
--os-project-name OS_PROJECT_NAME, --os-tenant-name OS_PROJECT_NAME
Project name to scope to
--os-project-domain-id OS_PROJECT_DOMAIN_ID
Domain ID containing project
--os-project-domain-name OS_PROJECT_DOMAIN_NAME
Domain name containing project
--os-trust-id OS_TRUST_ID
Trust ID
--os-default-domain-id OS_DEFAULT_DOMAIN_ID
Optional domain ID to use with v3 and v2 parameters.
It will be used for both the user and project domain
in v3 and ignored in v2 authentication.
--os-default-domain-name OS_DEFAULT_DOMAIN_NAME
Optional domain name to use with v3 API and v2
parameters. It will be used for both the user and
project domain in v3 and ignored in v2 authentication.
--os-user-id OS_USER_ID
User id
--os-username OS_USERNAME, --os-user-name OS_USERNAME
Username
--os-user-domain-id OS_USER_DOMAIN_ID
User's domain id
--os-user-domain-name OS_USER_DOMAIN_NAME
User's domain name
--os-password OS_PASSWORD
User's password
API Connection Options:
Options controlling the HTTP API Connections
--insecure Explicitly allow client to perform "insecure" TLS
(https) requests. The server's certificate will not be
verified against any certificate authorities. This
option should be used with caution.
--os-cacert <ca-certificate>
Specify a CA bundle file to use in verifying a TLS
(https) server certificate. Defaults to
env[OS_CACERT].
--os-cert <certificate>
Defaults to env[OS_CERT].
--os-key <key> Defaults to env[OS_KEY].
--timeout <seconds> Set request timeout (in seconds).
--collect-timing Collect per-API call timing information.
Service Options:
Options controlling the specialization of the API Connection from
information found in the catalog
--os-service-type <name>
Service type to request from the catalog
--os-service-name <name>
Service name to request from the catalog
--os-interface <name>
API Interface to use [public, internal, admin]
--os-region-name <name>
Region of the cloud to use
--os-endpoint-override <name>
Endpoint to use instead of the endpoint in the catalog
--os-api-version <name>
Which version of the service API to use
Example
@ -54,21 +134,28 @@ Example
To use Flame you can provide yours OpenStack credentials as arguments::
$ flame --username arezmerita --password password \
--project project-arezmerita --auth_url https://example.com/v2.0/
$ flame --os-username 'user_name' \
--os-password 'password' \
--os-project-name 'project_name' \
--os-auth-url 'http://<Keystone_host>:5000/v2.0'
Or a token and a tenant::
$ flame --username arezmerita --os-auth-token keystonetoken \
--project project-arezmerita --auth_url https://example.com/v2.0/
$ flame --os-auth-type 'token' \
--os-token 'token_id' \
--os-auth-url 'http://<Keystone_host>:5000/v2.0'
To establish a two-way SSL connection with the identity service ::
$flame --username arezmerita --os-auth-token keystonetoken \
--project project-arezmerita --auth_url https://example.com/v2.0/
--os-cert <path/to/certificate> --os-key <path/to/key>
$flame --os-username 'user_name' \
--os-password 'password' \
--os-project-name 'project_name' \
--os-auth_url http://<Keystone_host>:5000/v2.0 \
--os-cert <path/to/certificate> \
--os-key <path/to/key>
Or you can source your OpenStack RC file and use Flame without arguments::
$ source credential.rc
$ flame

21
flameclient/LICENSE.txt Normal file
View File

@ -0,0 +1,21 @@
This software is released under the MIT License.
Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,20 +1,30 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
# This software is released under the MIT License.
#
# http://www.apache.org/licenses/LICENSE-2.0
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import pbr.version
try:
__version__ = pbr.version.VersionInfo('flameclient').version_string()
except Exception:
except Exception: # pylint: disable=W0703
__version__ = 'unknown'

View File

@ -1,47 +0,0 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2014 Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from flameclient.flame import TemplateGenerator # noqa
class Client(object):
def __init__(self, username, password, tenant_name, auth_url, auth_token,
**kwargs):
self.template_generator = TemplateGenerator(username, password,
tenant_name, auth_url,
auth_token,
**kwargs)
def generate(self, exclude_servers, exclude_volumes, exclude_keypairs,
generate_stack_data, extract_ports=False,
exclude_secgroups=False):
self.template_generator.extract_vm_details(exclude_servers,
exclude_volumes,
exclude_keypairs,
generate_stack_data,
extract_ports,
exclude_secgroups
)
self.template_generator.extract_data()
return self.template_generator.heat_template_and_data()

View File

@ -2,7 +2,7 @@
# This software is released under the MIT License.
#
# Copyright (c) 2014 Cloudwatt
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@ -22,95 +22,59 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from __future__ import print_function
import argparse
import os
from flameclient import client
from flameclient import flame
def main(args=None):
desc = "Heat template and data file generator"
parser = argparse.ArgumentParser(description=desc)
parser.add_argument("--username", type=str,
default=os.environ.get("OS_USERNAME"),
help="A user name with access to the project. "
"Defaults to env[OS_USERNAME]")
parser.add_argument("--password", type=str,
default=os.environ.get("OS_PASSWORD"),
help="The user's password. "
"Defaults to env[OS_PASSWORD]")
parser.add_argument("--project", type=str,
default=os.environ.get("OS_TENANT_NAME"),
help="Name of project. "
"Defaults to env[OS_TENANT_NAME]")
parser.add_argument("--region",
default=os.environ.get("OS_REGION_NAME"),
help="Name of region. "
"Defaults to env[OS_REGION_NAME]")
parser.add_argument("--auth_url", type=str,
default=os.environ.get("OS_AUTH_URL"),
help="Authentication URL. "
"Defaults to env[OS_AUTH_URL].")
parser.add_argument("--os-auth-token", type=str,
default=os.environ.get("OS_AUTH_TOKEN"),
help="User's auth token. "
"Defaults to env[OS_AUTH_TOKEN].")
parser.add_argument('--insecure', action='store_true', default=False,
help="Explicitly allow clients to perform"
"\"insecure\" SSL (https) requests. The "
"server's certificate will not be verified "
"against any certificate authorities. This "
"option should be used with caution.")
parser.add_argument("--endpoint_type", type=str,
default=os.environ.get("OS_ENDPOINT_TYPE",
"publicURL"),
help="Defaults to env[OS_ENDPOINT_TYPE] or publicURL")
parser.add_argument("--os-cert", type=str, metavar='<certificate>',
default=os.environ.get("OS_CERT"),
help="User's certificate. "
"Defaults to env[OS_CERT].")
parser.add_argument("--os-key", type=str, metavar='<key>',
default=os.environ.get("OS_KEY"),
help="User's key. "
"Defaults to env[OS_KEY].")
parser.add_argument('--exclude-servers', action='store_true',
default=False,
help="Do not export in template server resources")
parser.add_argument('--exclude-volumes', action='store_true',
default=False,
help="Do not export in template volume resources")
parser.add_argument('--exclude-keypairs', action='store_true',
default=False,
help="Do not export in template key pair resources")
parser.add_argument('--generate-stack-data', action='store_true',
default=False,
help="In addition to template, generate Heat "
"stack data file.")
parser.add_argument('--extract-ports', action='store_true',
default=False,
help="Export the tenant network ports")
parser.add_argument('--exclude-secgroups', action='store_true',
default=False,
help="Do not export in template "
"security group resources")
def main():
"""Flame heat template generation
args = parser.parse_args()
flame = client.Client(args.username, args.password,
args.project, args.auth_url,
args.os_auth_token,
cert=args.os_cert, key=args.os_key,
region_name=args.region,
endpoint_type=args.endpoint_type,
insecure=args.insecure)
template = flame.template_generator
template.extract_vm_details(args.exclude_servers,
args.exclude_volumes,
args.exclude_keypairs,
args.generate_stack_data,
args.extract_ports,
args.exclude_secgroups)
template.extract_data()
print("### Heat Template ###")
print(template.heat_template_and_data())
Flame can be used with a shade or openstack sdk instance, and options can
be passed as option kwargs.
ex:
from flameclient.session import get_shade
from flameclient import flame
auth_kwargs = {
'auth_type': u'password',
'auth_url': 'https://your_cloud_identity_url/v2.0',
'interface': 'public',
'password': 'YourPassword',
'project_id': 'YourProjectID',
'project_name': 'YourProjectName',
'region_name': 'region_one',
'username': 'YourUserName'
}
cloud = get_shade(**auth_kwargs)
Or instead of kwargs if you want to use environment variables:
cloud = get_shade(load_envvars=True)
Then:
generator = flame.TemplateGenerator(
connection=cloud,
options=dict(
include_constraint=True,
extract_ports=True,
generate_adoption_data=True,
no_threads=True)
)
generator.extract_data()
print(generator.heat_template_and_data())
Passing a shade or openstack sdk instance and option kwargs allows you to
integrate shade in other projects.
"""
template_generator = flame.TemplateGenerator()
template_generator.extract_data()
template_generator.output_template_and_data()
if __name__ == '__main__':
main()

View File

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
try:
# This is not handled by six.moves yet:
# https://github.com/benjaminp/six/issues/155
from collections.abc import * # noqa pylint: disable=W0401,W0614
from collections.abc import __all__ # noqa
except ImportError:
from _abcoll import * # noqa pylint: disable=W0401,W0614
from _abcoll import __all__ # noqa

File diff suppressed because it is too large Load Diff

98
flameclient/logs.py Normal file
View File

@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import logging
from logging.config import dictConfig
import sys
COLOR_LOGS = sys.stdout.isatty()
class CWColorFormatter(logging.Formatter):
"""Special Formatter adding color to logs.
color is not added if settings.DEBUG is True to prevent syslog cluttering
when in production.
This Formatter is just Candy for developers.
"""
LEVEL_COLORS = {
logging.NOTSET: '\033[01;0m', # Reset color
logging.DEBUG: '\033[00;32m', # GREEN
logging.INFO: '\033[00;36m', # CYAN
# Where did this one go?:
# logging.AUDIT: '\033[01;36m', # BOLD CYAN
logging.WARN: '\033[01;33m', # BOLD YELLOW
logging.ERROR: '\033[01;31m', # BOLD RED
logging.CRITICAL: '\033[01;31m', # BOLD RED
}
reset_color = '\033[01;0m'
def format(self, record):
if COLOR_LOGS:
record.reset_color = self.reset_color
record.color = self.LEVEL_COLORS[record.levelno]
else:
# We do not want colors in production because syslog does not
# handle them.
record.reset_color = ''
record.color = ''
return super(CWColorFormatter, self).format(record)
LOGGING_CONFIG = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'()': CWColorFormatter,
'format': '%(color)s%(levelname)s: %(pathname)s %(funcName)s %(lineno)d:%(reset_color)s %(message)s' # noqa
},
},
'handlers': {
'default': {
'level': 'DEBUG',
'formatter': 'standard',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'': {
'handlers': ['default'],
'level': 'INFO',
'propagate': True
},
'flameclient': {
'handlers': ['default'],
# This can be overriden in the config's logging section
'level': 'INFO',
'propagate': False
},
}
}
dictConfig(LOGGING_CONFIG)

View File

@ -1,228 +0,0 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2014 Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from cinderclient.v1 import client as cinder_client
from keystoneclient.v2_0 import client as keystone_client
from neutronclient.v2_0 import client as neutron_client
from novaclient import client as nova_client
class KeystoneManager(object):
"""Manages Keystone queries."""
_client = None
def __init__(self, username, password, project, auth_url, insecure,
endpoint_type='publicURL', cert=None, key=None,
region_name=None, auth_token=None):
self.username = username
self.password = password
self.project = project
self.auth_url = auth_url
self.cert = cert
self.key = key
self.insecure = insecure
self.region_name = region_name
self.endpoint_type = endpoint_type
self.auth_token = auth_token
def authenticate(self):
self.client().authenticate()
self.auth_token = self.client().auth_token
def client(self):
if not self._client:
self._client = keystone_client.Client(
username=self.username,
password=self.password,
tenant_name=self.project,
auth_url=self.auth_url,
cert=self.cert,
key=self.key,
region_name=self.region_name,
insecure=self.insecure,
endpoint_type=self.endpoint_type,
token=self.auth_token)
return self._client
def set_client(self, client):
self._client = client
def get_token(self):
return self.client().auth_token
def get_endpoint(self, service_type, endpoint_type="publicURL"):
catalog = self.client().service_catalog.get_endpoints()
return catalog[service_type][0][endpoint_type]
def get_project_id(self):
return self.client().project_id
class NeutronManager(object):
_client = None
_project_id = None
def __init__(self, keystone_mgr):
self.keystone_mgr = keystone_mgr
def client(self):
if not self._client:
# Create the client
self._client = neutron_client.Client(
auth_url=self.keystone_mgr.auth_url,
insecure=self.keystone_mgr.insecure,
endpoint_url=self.keystone_mgr.get_endpoint('network'),
token=self.keystone_mgr.auth_token)
if not self._project_id:
self._project_id = self.keystone_mgr.get_project_id()
return self._client
def set_client(self, client):
self._client = client
def set_project_id(self, project_id):
self._project_id = project_id
def router_list(self):
return filter(self._owned_resource,
self.client().list_routers()['routers'])
def router_interfaces_list(self, router):
return self._client.list_ports(device_id=router['id'])['ports']
def port_list(self):
return self.client().list_ports()['ports']
def network_list(self):
return filter(self._owned_resource,
self.client().list_networks()['networks'])
def secgroup_list(self):
return filter(self._owned_resource,
self.client().list_security_groups()['security_groups'])
def floatingip_list(self):
return filter(self._owned_resource,
self.client().list_floatingips()['floatingips'])
def subnet_list(self):
return filter(self._owned_resource,
self.client().list_subnets()['subnets'])
def _owned_resource(self, res):
# Only considering resources owned by project
return res['tenant_id'] == self._project_id
class NovaManager(object):
"""Manage nova resources."""
_client = None
def __init__(self, keystone_mgr):
self.keystone_mgr = keystone_mgr
def client(self):
if not self._client:
self._client = nova_client.Client(
'2',
self.keystone_mgr.username,
self.keystone_mgr.auth_token,
self.keystone_mgr.project,
self.keystone_mgr.auth_url,
region_name=self.keystone_mgr.region_name,
insecure=self.keystone_mgr.insecure,
endpoint_type=self.keystone_mgr.endpoint_type,
auth_token=self.keystone_mgr.auth_token
)
return self._client
def set_client(self, client):
self._client = client
def server_list(self):
return self.client().servers.list()
def floating_ip_list(self):
return self.client().floating_ips.list()
def flavor_list(self):
return self.client().flavors.list()
def flavor_get(self, id):
return self.client().flavors.get(id)
def keypair_list(self):
return self.client().keypairs.list()
def keypair_show(self, keypair):
return self.client().keypairs.get(keypair)
def server_security_group_list(self, server):
return self.client().servers.list_security_group(server)
def servergroup_list(self):
return self.client().server_groups.list(True)
class CinderManager(object):
"""Manage Cinder resources."""
_client = None
def __init__(self, keystone_mgr):
self.keystone_mgr = keystone_mgr
self.defined = True
def client(self):
if self.defined and not self._client:
try:
cinder_url = self.keystone_mgr.get_endpoint("volumev2")
except KeyError:
cinder_url = self.keystone_mgr.get_endpoint("volume")
client = cinder_client.Client(
self.keystone_mgr.username,
self.keystone_mgr.auth_token,
project_id=self.keystone_mgr.project,
auth_url=cinder_url,
http_log_debug=True,
insecure=self.keystone_mgr.insecure
)
client.client.auth_token = self.keystone_mgr.auth_token
client.client.management_url = cinder_url
self._client = client
return self._client
def set_client(self, client):
self._client = client
def volume_list(self):
volumes = []
client = self.client()
if client:
for vol in client.volumes.list():
volumes.append(client.volumes.get(vol.id))
return volumes
def snapshot_list(self):
client = self.client()
return client.volume_snapshots.list() if client else []

View File

@ -0,0 +1,574 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import abc
import logging
import six
from six.moves import UserDict
from flameclient.utils import camel_to_snake
from flameclient.utils import ClassProperty
from flameclient.utils import clean_dict
from flameclient.utils import load_resource_entry_points
from flameclient.utils import load_resource_modules
from flameclient.utils import munchify
LOG = logging.getLogger(__name__)
class BaseHotResource(object):
"""Describes a heat resource from parameters
:param ResourceManager manager:
A ResourceManager instance.
This instance is necessary to have access to the manager's
openstack.connection.Connection instance
:param string type: The name of the openstack resource type.
e.g.: "OS::Nova::Server" etc...
:param string id: The openstack resource id.
:param dict properties: The openstack resource properties.
"""
id = None
manager = None
parameters = None
properties = None
type = None
def __init__(self, manager, name, type=None, id=None, properties=None): # noqa pylint: disable=W0622
if not isinstance(manager, ResourceManager):
raise TypeError(
"manager needs to be a flameclient.resources.ResourceManager "
"instance. Received '%s' (%s) instead" % (
manager, self.__type(manager)
)
)
self.manager = manager
self.name = name
if type:
self.type = type
self.id = id
self.status = 'COMPLETE'
self.properties = properties or {}
self.parameters = {}
@staticmethod
def __type(*args, **kwargs):
"""Needed to call the builtin type function when overriden"""
return type(*args, **kwargs)
@property
def connection(self):
return self.manager.connection
@property
def conn(self):
return self.connection
@property
def cloud(self):
return self.manager.cloud
@property
def generator(self):
return self.manager.generator
@property
def managers(self):
return self.generator.managers
@property
def api(self):
return self.generator.api
@property
def options(self):
return self.generator.options
@property
def kwargs(self):
return self.manager.kwargs
def add_parameter(
self, name, description, parameter_type='string', constraints=None,
default=None
):
data = {
'type': parameter_type,
'description': description,
}
if constraints and self.options.include_constraints:
data['constraints'] = constraints
if default:
data['default'] = default
self.parameters[name] = data
@property
def clean_properties(self):
return clean_dict(self.properties)
@property
def template_resource(self):
return {
self.name: {
'type': self.type,
'properties': self.clean_properties
}
}
@property
def clean_parameters(self):
return clean_dict(self.parameters)
@property
def template_parameter(self):
return self.parameters
@property
def stack_resource(self):
if self.id is None:
return {}
return {
self.name: {
'status': self.status,
'name': self.name,
'resource_data': {},
'resource_id': self.id,
'action': 'CREATE',
'type': self.type,
'metadata': {}
}
}
def generator_memoize(self, method, *args, **kwargs):
return self.manager.generator_memoize(method, *args, **kwargs)
@six.add_metaclass(abc.ABCMeta)
class TypedHotResource(BaseHotResource):
"""Describes a heat resource from parameters with predefined type
:param ResourceManager manager:
A ResourceManager instance.
This instance is necessary to have access to the manager's
openstack.connection.Connection instance
:param string id: The openstack resource id.
:param dict properties: The openstack resource properties.
"""
def __init__(self, manager, name, id=None, properties=None): # noqa pylint: disable=W0622
super(TypedHotResource, self).__init__(
manager, name, type=None, id=id, properties=properties
)
@abc.abstractproperty
def type(self): # pylint: disable=E0202
"""The resource type
You need to set this property on subclasses.
"""
raise NotImplementedError
@six.add_metaclass(abc.ABCMeta)
class AdvancedHotResource(TypedHotResource, UserDict):
"""Describes a heat resource from openstack.resource.Resource instance
These advanced resources mays also have resources.
property_keys are properties to automatically extract from the resource.
:param ResourceManager manager:
A ResourceManager instance.
This instance is necessary to have access to the manager's
openstack.connection.Connection instance
:param string name: The name of the openstack resource.
e.g.: "My_server".
:param openstack.resource.Resource data:
An `openstack.resource.Resource` instance or a munch.Munch instance
representing the openstack resource returned by an openstack client or
returned by an `openstack.connection.Connection` instance's API call
method, or returned by a `shade.openstackcloud.OpenStackCloud`
instance's API call method.
:param dict properties: The openstack resource properties.
When subclassing this class, the `property_keys` tuple represents
property keys which will be automatically extracted from the resource,
allowing you to not pass any properties.
Since we inheritate from UserDict, we have a direct access to `self.data`.
More info here:
https://docs.python.org/3/library/collections.html#userdict-objects
The only difference is that we use a munch instead of dict for `self.data`
This means we can access `self.data[key]` with `self[key]` (UserDict) and
via `self.data.key` (munch).
"""
property_keys = ()
data = None
def __init__(self, manager, name, data, properties=None):
data = munchify(data)
final_properties = {}
# Automatically get properties from self.property_keys
for key in self.property_keys:
final_properties[key] = data[key]
# Override properties:
if properties:
final_properties.update(properties)
super(AdvancedHotResource, self).__init__(
manager, name, id=data.get('id'),
properties=final_properties
)
self.data = data
@six.add_metaclass(abc.ABCMeta)
class ResourceManager(object):
"""Resource manager to list specific openstack resources
:param flameclient.flame.TemplateGenerator generator:
A `flameclient.flame.TemplateGenerator` instance.
:param argparse.Namespace options:
argparse options.
args and kwargs are extra parameters which each resource can access in case
of developer needs.
This class needs to be subclassed. Every subclass which is imported will be
discovered by `resources.get_manager_classes()`
"""
generator = None
args = ()
kwargs = None
def __init__(self, generator, *args, **kwargs):
self.generator = generator
self.args = args
self.kwargs = kwargs or {}
@ClassProperty
def name(cls):
"""The resource manager name
You need to set this property on subclasses if the automatic naming
does not work.
ex.: If this class is calles FooBarsManager, this property will return
'foo_bars'
"""
return camel_to_snake(cls.__name__).rstrip('_manager')
@ClassProperty
def singular_name(cls):
"""The resource manager singular name
You need to set this property on subclasses if the automatic naming
does not work.
Override this if the singular of self.name does not consist in
in removing the trailing 's'.
"""
return cls.name.rstrip('s')
@property
def options(self):
return self.generator.options
@property
def connection(self):
return self.generator.connection
@property
def conn(self):
"""Give a short name access"""
return self.connection
@property
def cloud(self):
return self.generator.cloud
@property
def managers(self):
return self.generator.managers
@classmethod
def add_arguments(cls, parser):
"""Add parser argparse.ArgumentParser arguments
Use this method in your subclasses if you want to add any specific
argparse arguments. These arguments will then be available on
`self.options`.
:param argparse.ArgumentParser parser: An argparse.ArgumentParser
instance
:returns: An argparse.ArgumentParser parser instance
"""
return parser
@abc.abstractproperty
def api_resources(self):
"""Implement this property to return api resources
This property will be automatically set as an attribute on
`generator.api` and `self.api` with `self.name`.
e.g.: if this class has `self.name` set to 'routers', the
`self.generator.api.routers` attribute and `self.api.routers`
attribute will return the results of this property.
Consider using the `utils.memoized_property` decorator on the
property. This is because this property should be computed only once
It's the reason why it's a property and not a method.
Also consider using self.generator_memoize on API calls.
This property should return a Munch instance generated by
`utils.data_list_to_dict` on a list of `openstack.resource.Resource`
instances in order to be consistent with all managers
the result has to be in the following form:
Munch(
{
'some_id': Munch({}),
'other_id': Munch{},
}
)
The `get_resource_name` expects to find a `.enum` attribute on
each munch value. You will need to override `get_resource_name` if
you have a different data format.
"""
raise NotImplementedError
def get_api_resources(self):
return self.api_resources
@property
def api(self):
"""Direct access to all managers' api resources
e.g.: if there is a FooManager (with FooManager.name = 'foo'),
`self.api.foo` will give access to `FooManager.api_resources`
if the current instance and a FooManager instance are in a ManagerList
on a TemplateGenerator.
This allows access to each manager's api resources from any other
manager.
For this to work it implies that TemplateGenerator.api is a
DirectManagerApiResourceAccess instance.
"""
return self.generator.api
@abc.abstractmethod
def get_hot_resources(self):
"""Implement this method to return resources
You need to take into account `self.options.no_threads` and not use
threads (e.g.: `concurrent.futures`, threading module, etc...) when it
is True.
"""
raise NotImplementedError
def get_resource_num(self, resource_id):
return self.api_resources[resource_id].enum
def get_resource_name(self, resource_id):
return '%s_%d' % (
self.singular_name, self.get_resource_num(resource_id)
)
def generator_memoize(self, method, *args, **kwargs):
return self.generator.generator_memoize(method, *args, **kwargs)
@ClassProperty
def post_priority(cls):
return 100
def post_process(self):
"""This method is called after get_hot_resources of all managers
Use this method to perform actions after processing.
This method will be called on all managers sorted by the order of
their self.post_priority number.
"""
pass
def post_process_hot_resources(self, resources): # pylint: disable=R0201
"""This method is called after get_hot_resources of all managers
Use this method to perform post processing resources modifications.
:param list resources: the self.generator.resources attribute after
all managers' `get_hot_resources` methods have
been called.
:returns: A modified resources list (or a new resources list).
A resources list is a list containing resources returned by
each manager's `get_hot_resources` method.
This method will be called on all managers sorted by the order of
their self.post_priority number.
This allows you to modify the list aof resources returned by managers.
"""
return resources
def post_process_heat_template(self, template): # pylint: disable=R0201
"""This method is called after get_hot_resources of all managers
Use this method to perform post processing template modifications.
:param dict template: the self.generator.template attribute after
all managers' `get_hot_resources` methods have
been called.
:returns: The template dictionary which the generator will use to
render the flame template. If ou need to create a blank
template use `self.generator.get_new_template()` to
initialise it.
You need to return a heat template (in python dictionary format)
This method will be called on all managers sorted by the order of
their self.post_priority number.
"""
return template
def post_process_adoption_data(self, adoption_data): # noqa pylint: disable=R0201
"""This method is called after get_hot_resources of all managers
Use this method to perform post processing adoption_data modifications.
:param dict adoption_data: the self.generator.adoption_data attribute
after all managers' `get_hot_resources`
methods have been called.
:returns: The adoption_data dictionary which the generator will use to
render the flame adoption data. If ou need to create a blank
template use `self.generator.get_new_adoption_data()` to
initialise it.
This method will be called on all managers sorted by the order of
their self.post_priority number.
"""
return adoption_data
class ManagerList(list):
def __getattr__(self, name):
for manager in self:
if manager.name == name:
return manager
raise AttributeError(
"'%s' has no '%s' manager" % (
self.__name__, name
)
)
def __setattr__(self, name, value):
if isinstance(value, ResourceManager):
if name != value.name:
raise ValueError(
"'%s'.name != '%s'" % (value, name)
)
self.append(value)
raise TypeError(
"'%s' '%s' has to be a ResourceManager instance instead of %s" % (
value, name, type(value)
)
)
def __delattr__(self, name):
for num, manager in enumerate(self):
if manager.name == name:
self.pop(num)
raise AttributeError(
"'%s' has no '%s' manager" % (
self.__name__, name
)
)
class DirectManagerApiResourceAccess(object):
"""Give direct access to a manager's api_resources attribute
This needs to be set as the 'api' attribute on the TemplateGenerator
class.
"""
instance = None
owner = None
def __get__(self, instance, owner):
if instance:
self.instance = instance
self.owner = owner
return self
def __getattr__(self, name):
if self.instance:
for manager in self.instance.managers:
if manager.name == name:
return manager.api_resources
raise AttributeError(
"'%s' has no '%s' manager in self.managers" % (
self.owner.__name__, name
)
)
def get_manager_classes():
"""List all subclasses of ResourceManager
if any loaded python module contains a ResourceManager subclass, it will be
discovered.
Make sure to load any module containing the subclass if you want it to be
discoverable.
You can use `utils.load_resource_modules(dirname) or
`utils.load_resource_entry_points(name='openstack_flame')` to load modules
containing ResourceManager subclasses before using this method.
"""
load_resource_modules(__file__)
load_resource_entry_points()
return ResourceManager.__subclasses__()

View File

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from flameclient import resources as base_resources
from flameclient.utils import data_list_to_dict
from flameclient.utils import memoized_property
class FlavorsManager(base_resources.ResourceManager):
"""Used only to provide api resources. Heat does not create flavors."""
@staticmethod
def add_resource_flavor(resource):
data = resource.data
manager = resource.manager
flavor_parameter_name = "%s_flavor" % resource.name
description = "Flavor to use for %s %s" % (
manager.singular_name, resource.name
)
default = data.flavor['id']
resource.add_parameter(
flavor_parameter_name, description, default=default
)
resource.properties['flavor'] = {'get_param': flavor_parameter_name}
@memoized_property
def api_resources(self):
return data_list_to_dict(
self.generator_memoize(self.conn.compute.flavors)
)
def get_hot_resources(self):
return []

View File

@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import six
from flameclient import resources as base_resources
from flameclient.utils import data_list_to_dict
from flameclient.utils import memoized_property
class FloatingIP(base_resources.AdvancedHotResource):
type = 'OS::Neutron::FloatingIP'
def __init__(self, manager, name, floating_ip, properties=None):
super(FloatingIP, self).__init__(
manager, name, floating_ip, properties
)
net_param_name = "external_network_for_%s" % self.name
self.properties['floating_network_id'] = {'get_param': net_param_name}
description = "Network to allocate floating IP from"
constraints = [{'custom_constraint': "neutron.network"}]
default = self.data['floating_network_id']
self.add_parameter(net_param_name, description,
constraints=constraints,
default=default)
@property
def association(self):
return self.managers.ports.get_fip_association(self) \
or self.managers.servers.get_fip_association(self)
class FloatingIpsManager(base_resources.ResourceManager):
@memoized_property
def api_resources(self):
return data_list_to_dict(
self.generator_memoize(self.conn.network.ips)
)
def get_hot_resources(self):
floating_ips = [
FloatingIP(self, self.get_resource_name(fip_id), floating_ip)
for fip_id, floating_ip in six.iteritems(self.api.floating_ips)
]
floating_ip_associations = [
fip.association for fip in floating_ips
if fip.association
]
return floating_ips + floating_ip_associations

View File

@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from flameclient import resources as base_resources
from flameclient.utils import data_list_to_dict
from flameclient.utils import memoized_property
class ImagesManager(base_resources.ResourceManager):
"""Used only to provide api resources. Heat does not create images."""
@staticmethod
def add_resource_image(resource):
data = resource.data
manager = resource.manager
if data.image:
image_parameter_name = "%s_image" % resource.name
description = (
"Image to use for %s %s" % (
manager.singular_name, resource.name
)
)
constraints = [{'custom_constraint': "glance.image"}]
resource.add_parameter(image_parameter_name, description,
default=data.image['id'],
constraints=constraints)
resource.properties['image'] = {'get_param': image_parameter_name}
@memoized_property
def api_resources(self):
return data_list_to_dict(
# self.conn.compute.images gives detailed images
self.generator_memoize(self.conn.image.images)
)
def get_hot_resources(self):
return []

View File

@ -0,0 +1,85 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import six
from flameclient import resources as base_resources
from flameclient.utils import data_list_to_dict
from flameclient.utils import memoized_property
class KeyPair(base_resources.AdvancedHotResource):
type = 'OS::Nova::KeyPair'
property_keys = ('name', 'public_key',)
class KeypairsManager(base_resources.ResourceManager):
def add_resource_keypair(self, resource):
data = resource.data
manager = resource.manager
if data.key_name:
if (
self.options.exclude_keypairs or
data.key_name not in self.api.keypairs
):
key_parameter_name = "%s_key" % resource.name
description = (
"Key for %s %s" % (manager.singular_name, resource.name)
)
constraints = [{'custom_constraint': "nova.keypair"}]
resource.add_parameter(key_parameter_name, description,
default=data.key_name,
constraints=constraints)
resource.properties['key_name'] = {
'get_param': key_parameter_name
}
else:
resource.properties['key_name'] = {
'get_resource': self.get_resource_name(data.key_name)
}
@classmethod
def add_arguments(cls, parser):
parser.add_argument('--exclude-keypairs', action='store_true',
default=False,
help="Do not export key pair resources."
)
return parser
@memoized_property
def api_resources(self):
return data_list_to_dict(
self.generator_memoize(self.conn.compute.keypairs)
)
def get_hot_resources(self):
if not self.options.exclude_keypairs:
return [
KeyPair(self, self.get_resource_name(resource.id), resource)
for resource in six.itervalues(self.api.keypairs)
]
return []

View File

@ -0,0 +1,90 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import six
import netaddr
from flameclient import resources as base_resources
from flameclient.utils import data_list_to_dict
from flameclient.utils import memoized_property
class Network(base_resources.AdvancedHotResource):
type = 'OS::Neutron::Net'
property_keys = ('name', 'admin_state_up', 'shared')
class NetworksManager(base_resources.ResourceManager):
def add_resource_networks(self, resource):
addresses = resource.data.addresses
networks = []
for net_name in addresses:
ip = addresses[net_name][0]['addr']
for subnet in six.itervalues(self.api.subnets):
if netaddr.IPAddress(ip) in netaddr.IPNetwork(subnet['cidr']):
for network in six.itervalues(self.api.networks):
if (network['name'] == net_name and
network['id'] == subnet['network_id']):
net = self.get_resource_name(subnet['network_id'])
networks.append({'network': {'get_resource': net}})
if networks:
resource.properties['networks'] = networks
@memoized_property
def all_networks(self):
return data_list_to_dict(
self.generator_memoize(self.conn.network.networks)
)
@property
def api_resources(self):
return self.all_networks
@memoized_property
def external_networks(self):
return data_list_to_dict(
(
network for network in six.itervalues(self.all_networks)
if network['router:external']
),
enum=False # Do not overwrite enumeration done in all_networks
)
@memoized_property
def internal_networks(self):
return data_list_to_dict(
(
network for network in six.itervalues(self.all_networks)
if network.id not in self.external_networks
),
enum=False # Do not overwrite enumeration done in all_networks
)
def get_hot_resources(self):
return [
Network(self, self.get_resource_name(network.id), network)
for network in six.itervalues(self.internal_networks)
]

View File

@ -0,0 +1,137 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import six
from flameclient import resources as base_resources
from flameclient.utils import data_list_to_dict
from flameclient.utils import memoized_property
class Port(base_resources.AdvancedHotResource):
type = 'OS::Neutron::Port'
property_keys = ('admin_state_up', 'mac_address', 'device_owner')
def __init__(self, manager, name, port, properties=None):
super(Port, self).__init__(manager, name, port, properties)
fixed_ips = []
for fixed_ip_dict in port['fixed_ips']:
subnet_id = fixed_ip_dict['subnet_id']
subnet_resource_name = self.managers.subnets.get_resource_name(
subnet_id
)
fixed_ip_resource = {
u'subnet_id': {'get_resource': subnet_resource_name},
u'ip_address': fixed_ip_dict['ip_address']
}
fixed_ips.append(fixed_ip_resource)
net_resource_name = self.managers.networks.get_resource_name(
port['network_id']
)
self.properties.update(
{'network_id': {'get_resource': net_resource_name},
'fixed_ips': fixed_ips}
)
if port['name'] != '':
# This port has a name
self.properties['name'] = port['name']
self.managers.security_groups.add_resource_secgrp_props_and_params(
self
)
class NeutronFloatingIpAssociation(base_resources.TypedHotResource):
type = 'OS::Neutron::FloatingIPAssociation'
class PortsManager(base_resources.ResourceManager):
def get_fip_association(self, resource):
ip = resource.data
manager = resource.manager
if ip['port_id'] and self.options.extract_ports:
port_resource_name = self.get_resource_name(ip['port_id'])
properties = {
'floatingip_id': {'get_resource': resource.name},
'port_id': {'get_resource': port_resource_name}
}
resource_num = manager.get_resource_num(resource.id)
fip_assoc_id = ("%s:%s" % (ip['id'], ip['port_id']))
return NeutronFloatingIpAssociation(
manager,
"floatingip_association_%d" % resource_num,
fip_assoc_id,
properties
)
def get_ports_for_resource(self, resource):
ports = []
for port in six.itervalues(self.api.ports):
if port['device_id'] == resource.data.id:
ports.append(self.get_resource_name(port.id))
return ports
def add_resource_ports_or_secgroups_and_networks(self, resource):
if self.options.extract_ports:
ports = [{"port": {"get_resource": port}}
for port in self.get_ports_for_resource(resource)]
if ports:
resource.properties['networks'] = ports
else:
self.managers.security_groups \
.add_resource_secgrp_props_and_params(resource)
self.managers.networks.add_resource_networks(resource)
@classmethod
def add_arguments(cls, parser):
parser.add_argument('--extract-ports', action='store_true',
default=False,
help="Export the tenant network ports.")
return parser
@memoized_property
def api_resources(self):
return data_list_to_dict(
self.generator_memoize(self.conn.network.ports)
)
@memoized_property
def port_resources(self):
return [
Port(self, self.get_resource_name(port.id), port)
for port in six.itervalues(self.api.ports)
]
def get_hot_resources(self):
if self.options.extract_ports:
return [
port for port in self.port_resources
if port['device_owner'].startswith('compute:')
]
return []

View File

@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import six
from flameclient import resources as base_resources
from flameclient.utils import data_list_to_dict
from flameclient.utils import memoized_property
from flameclient.utils import munchify
class RouterInterface(base_resources.TypedHotResource):
type = 'OS::Neutron::RouterInterface'
class RouterGateway(base_resources.TypedHotResource):
type = 'OS::Neutron::RouterGateway'
class Router(base_resources.AdvancedHotResource):
type = 'OS::Neutron::Router'
property_keys = ('name', 'admin_state_up')
@memoized_property
def os_ports(self):
return munchify(self.generator_memoize(
self.conn.network.ports, device_id=self['id']
))
@memoized_property
def router_interfaces(self):
router_interfaces = []
for n, port in enumerate(self.os_ports):
if port['device_owner'] != "network:router_interface":
continue
resource_name = "%s_interface_%d" % (self.name, n)
subnet_resource_name = self.managers.subnets.get_resource_name(
port['fixed_ips'][0]['subnet_id']
)
resource_id = ("%s:subnet_id=%s" %
(port['device_id'],
port['fixed_ips'][0]['subnet_id']))
properties = {
'subnet_id': {'get_resource': subnet_resource_name},
'router_id': {'get_resource': self.name}
}
router_interfaces.append(
RouterInterface(
self.manager, resource_name, resource_id, properties)
)
return router_interfaces
@memoized_property
def router_gateway(self):
if self['external_gateway_info']:
router_external_network_name = ("%s_external_network" % self.name)
external_network = self['external_gateway_info']['network_id']
properties = {
'router_id': {'get_resource': self.name},
'network_id': {'get_param': router_external_network_name}
}
gateway = RouterGateway(self.manager, "%s_gateway" % self.name,
"%s:%s" % (self['id'], external_network),
properties)
description = "Router external network"
constraints = [{'custom_constraint': "neutron.network"}]
gateway.add_parameter(router_external_network_name, description,
constraints=constraints,
default=external_network)
return gateway
class RoutersManager(base_resources.ResourceManager):
@memoized_property
def api_resources(self):
return data_list_to_dict(
self.generator_memoize(self.conn.network.routers)
)
routers = api_resources
def get_hot_resources(self):
resources = []
for rid, router in six.iteritems(self.api.routers):
resource = Router(self, self.get_resource_name(rid), router)
resources.append(resource)
resources.extend(resource.router_interfaces)
if resource.router_gateway:
resources.append(resource.router_gateway)
return resources

View File

@ -0,0 +1,175 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from copy import deepcopy
import six
from flameclient import collections_abc
from flameclient import resources as base_resources
from flameclient.utils import clean_dict
from flameclient.utils import data_list_to_dict
from flameclient.utils import memoized_property
class SecurityGroup(base_resources.AdvancedHotResource):
type = 'OS::Neutron::SecurityGroup'
property_keys = ('description',)
def __init__(self, manager, name, data, properties=None):
super(SecurityGroup, self).__init__(
manager, name, data, properties
)
if data['name'] == 'default':
self.properties['name'] = '_default'
else:
self.properties['name'] = data['name']
self.properties['rules'] = self._build_rules(
data['security_group_rules']
)
def _build_rules(self, original_rules):
final_rules = []
for rule in original_rules:
new_rule = deepcopy(rule)
if new_rule['protocol'] == 'any':
del new_rule['protocol']
del new_rule['port_range_min']
del new_rule['port_range_max']
rg_id = new_rule['remote_group_id']
if rg_id is not None:
new_rule['remote_mode'] = "remote_group_id"
resource_name = self.manager.get_resource_name(rg_id)
if rg_id == new_rule['security_group_id']:
del new_rule['remote_group_id']
else:
new_rule['remote_group_id'] = {
'get_resource': resource_name
}
del new_rule['tenant_id']
del new_rule['id']
del new_rule['security_group_id']
final_rule = clean_dict(new_rule, clean_list=False)
final_rules.append(final_rule)
return final_rules
class SecurityGroupsManager(base_resources.ResourceManager):
@classmethod
def add_arguments(cls, parser):
parser.add_argument('--exclude-secgroups', action='store_true',
default=False,
help="Do not export the security group resources.")
return parser
@memoized_property
def api_resources(self):
return data_list_to_dict(
self.generator_memoize(self.conn.network.security_groups)
)
def get_security_group(self, name_or_id):
if isinstance(name_or_id, six.string_types):
try:
return self.api_resources[name_or_id]
except KeyError:
for sg in six.itervalues(self.api_resources):
if name_or_id == sg.name:
return sg
raise ValueError(
"No security group with '%s' id or name found"
)
elif isinstance(name_or_id, collections_abc.Mapping):
if 'id' in name_or_id:
return self.get_security_group(name_or_id['id'])
elif 'name' in name_or_id:
return self.get_security_group(name_or_id['name'])
else:
raise KeyError(
"%s has no 'id' or 'name' key" % name_or_id
)
else:
raise ValueError(
"%s has to be a string or dict with 'id' or 'name' key."
)
def get_resource_secgroups(self, resource):
return [
self.get_security_group(sg)
for sg in resource.data.security_groups
]
def add_resource_secgrp_props_and_params(self, resource):
"""Add security group properties and parameters to a resource
:param BaseHotResource resource: a BaseHotResource (or subclass)
instance
"""
if not self.options.exclude_secgroups:
manager = resource.manager
data = resource.data
security_groups = []
secgroup_default_parameter = None
for secgr in self.get_resource_secgroups(resource):
if secgr['name'] == 'default' and \
self.options.generate_adoption_data:
if not secgroup_default_parameter:
res_name = manager.get_resource_name(data['id'])
param_name = "%s_default_security_group" % res_name
description = (
"Default security group for %s %s" % (
manager.singular_name, resource['name']
)
)
default = secgr['id']
resource.add_parameter(
param_name, description, default=default
)
secgroup_default_parameter = {'get_param': param_name}
security_groups.append(secgroup_default_parameter)
else:
resource_name = self.get_resource_name(secgr['id'])
security_groups.append({'get_resource': resource_name})
if security_groups:
resource.properties['security_groups'] = security_groups
def get_hot_resources(self):
resources = []
if not self.options.exclude_secgroups:
for secgroup in six.itervalues(self.api.security_groups):
if secgroup['name'] == 'default' \
and self.options.generate_adoption_data:
continue
resources.append(
SecurityGroup(
self, self.get_resource_name(secgroup.id), secgroup
)
)
return resources

View File

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import six
from flameclient import resources as base_resources
from flameclient.utils import data_list_to_dict
from flameclient.utils import memoized_property
class ServerGroup(base_resources.AdvancedHotResource):
type = 'OS::Nova::ServerGroup'
property_keys = ('name', 'policies')
class ServerGroupsManager(base_resources.ResourceManager):
def add_resource_server_groups(self, resource):
server = resource.data
for servergroup in self.api_resources.values():
if server.id in servergroup.members:
hint = {
'group': {
'get_resource':
self.get_resource_name(servergroup.id)
}
}
resource.properties['scheduler_hints'] = hint
@memoized_property
def api_resources(self):
return data_list_to_dict(
self.generator_memoize(self.conn.compute.server_groups)
)
def get_hot_resources(self):
return [
ServerGroup(self, self.get_resource_name(sg.id), sg)
for sg in six.itervalues(self.api_resources)
]

View File

@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import six
from flameclient import resources as base_resources
from flameclient.utils import data_list_to_dict
from flameclient.utils import memoized_property
class Server(base_resources.AdvancedHotResource):
type = 'OS::Nova::Server'
property_keys = ('name',)
def __init__(self, manager, name, server, properties=None):
super(Server, self).__init__(manager, name, server, properties)
for property_name in ('config_drive', 'metadata'):
if server[property_name]:
self.properties[property_name] = server[property_name]
self.managers.flavors.add_resource_flavor(self)
self.managers.images.add_resource_image(self)
self.managers.keypairs.add_resource_keypair(self)
self.managers.ports.add_resource_ports_or_secgroups_and_networks(self)
self.managers.volumes.add_resource_attached_volumes(self)
self.managers.server_groups.add_resource_server_groups(self)
class NovaFloatingIpAssociation(base_resources.TypedHotResource):
type = 'OS::Nova::FloatingIPAssociation'
class ServersManager(base_resources.ResourceManager):
def get_fip_association(self, resource):
ip = resource.data
manager = resource.manager
if not self.options.exclude_servers and ip['port_id']:
server_id = self.api.ports[ip['port_id']]['device_id']
if server_id and server_id in self.api.servers:
server_resource_name = self.get_resource_name(server_id)
resource_num = manager.get_resource_num(resource.id)
properties = {
'floating_ip': {'get_resource': resource.name},
'server_id': {'get_resource': server_resource_name}
}
return NovaFloatingIpAssociation(
manager,
"floatingip_association_%d" % resource_num,
None,
properties
)
@classmethod
def add_arguments(cls, parser):
parser.add_argument('--exclude-servers', action='store_true',
default=False,
help="Do not export in template server resources.")
return parser
@memoized_property
def api_resources(self):
return data_list_to_dict(
self.generator_memoize(self.conn.compute.servers)
)
def get_hot_resources(self):
if not self.options.exclude_servers:
return [
Server(self, self.get_resource_name(server.id), server)
for server in six.itervalues(self.api.servers)
]
return []

View File

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import six
from flameclient import resources as base_resources
from flameclient.utils import data_list_to_dict
from flameclient.utils import memoized_property
class Subnet(base_resources.AdvancedHotResource):
type = 'OS::Neutron::Subnet'
property_keys = (
'name',
'allocation_pools',
'cidr',
'dns_nameservers',
'enable_dhcp',
'host_routes',
'ip_version',
)
def __init__(self, manager, name, data, properties=None):
super(Subnet, self).__init__(manager, name, data, properties)
net_name = self.managers.networks.get_resource_name(self['network_id'])
self.properties['network_id'] = {'get_resource': net_name}
class SubnetsManager(base_resources.ResourceManager):
@memoized_property
def api_resources(self):
return data_list_to_dict(
self.generator_memoize(self.conn.network.subnets)
)
def get_hot_resources(self):
return [
Subnet(self, self.get_resource_name(subnet.id), subnet)
for subnet in six.itervalues(self.api.subnets)
if subnet['network_id'] in self.managers.networks.internal_networks
]

View File

@ -0,0 +1,158 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import six
from flameclient import resources as base_resources
from flameclient.utils import data_list_to_dict
from flameclient.utils import memoized_property
class Volume(base_resources.AdvancedHotResource):
type = 'OS::Cinder::Volume'
property_keys = ('size', 'name')
def _add_source_volume(self):
volume = self.data
if volume.source_volid:
if volume.source_volid in self.api.volumes:
key = self.manager.get_resource_name(volume.source_volid)
self.properties['source_volid'] = {'get_resource': key}
else:
key = "%s_source_volid" % self.name
description = (
"Volume to create volume %s from" % self.name)
self.add_parameter(key, description)
self.properties['source_volid'] = {'get_param': key}
def _add_image(self):
volume = self.data
if volume.bootable and not volume.snapshot_id:
key = "%s_image" % self.name
description = "Image to create volume %s from" % self.name
constraints = [{'custom_constraint': "glance.image"}]
default = volume.volume_image_metadata['image_id']
self.add_parameter(key, description,
constraints=constraints,
default=default)
self.properties['image'] = {'get_param': key}
def _add_snapshot(self):
volume = self.data
if volume.snapshot_id:
key = "%s_snapshot_id" % self.name
self.properties['snapshot_id'] = {'get_param': key}
description = (
"Snapshot to create volume %s from" % self.name)
self.add_parameter(key, description,
default=volume.snapshot_id)
def _add_display_name(self):
volume = self.data
if hasattr(volume, 'display_name') and volume.display_name:
self.properties['name'] = volume.display_name
def _add_display_description(self):
volume = self.data
if (
hasattr(volume, 'display_description') and
volume.display_description
):
self.properties['description'] = volume.display_description
def _add_volume_type(self):
volume = self.data
if volume.volume_type and volume.volume_type != 'None':
key = "%s_volume_type" % self.name
description = (
"Volume type for volume %s" % self.name)
default = volume.volume_type
self.add_parameter(key, description, default=default)
self.properties['volume_type'] = {'get_param': key}
def _add_metadata(self):
volume = self.data
if volume.metadata:
self.properties['metadata'] = volume.metadata
def __init__(self, manager, name, volume, properties=None):
super(Volume, self).__init__(manager, name, volume, properties)
self._add_source_volume()
self._add_image()
self._add_display_name()
self._add_display_description()
self._add_volume_type()
self._add_metadata()
class VolumesManager(base_resources.ResourceManager):
def add_resource_attached_volumes(self, resource):
server = resource.data
manager = resource.manager
server_volumes = []
att_key = 'os-extended-volumes:volumes_attached'
for server_volume in server[att_key]:
volume = self.api.volumes[server_volume['id']]
volume_resource_name = self.get_resource_name(server_volume['id'])
device = volume.attachments[0]['device']
if not self.options.exclude_volumes:
server_volumes.append(
{'volume_id': {'get_resource': volume_resource_name},
'device_name': device})
else:
volume_parameter_name = ("volume_%s_%d" %
(server.name, volume.enum))
description = ("Volume for %s %s, device %s" %
(manager.singular_name, server.name, device))
server_volumes.append(
{'volume_id': {'get_param': volume_parameter_name},
'device_name': device})
resource.add_parameter(volume_parameter_name, description,
default=server_volume['id'])
if server_volumes:
# block_device_mapping_v2 is the new way of associating
# block devices to an instance
resource.properties['block_device_mapping_v2'] = server_volumes
@classmethod
def add_arguments(cls, parser):
parser.add_argument('--exclude-volumes', action='store_true',
default=False,
help="Do not export volume resources.")
return parser
@memoized_property
def api_resources(self):
return data_list_to_dict(
self.generator_memoize(self.conn.volume.volumes)
)
def get_hot_resources(self):
if not self.options.exclude_volumes:
return [
Volume(self, self.get_resource_name(volume.id), volume)
for volume in six.itervalues(self.api.volumes)
]
return []

278
flameclient/session.py Normal file
View File

@ -0,0 +1,278 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import argparse
import logging
import sys
from keystoneauth1 import loading
from keystoneauth1 import session as keystone_session
from openstack import connection as os_connection
# We get a subclass of openstack.config.loader.OpenStackConfig which is more
# complete:
from os_client_config.config import OpenStackConfig # noqa
from shade.openstackcloud import OpenStackCloud # noqa
from flameclient import utils
LOG = logging.getLogger(__name__)
def list_auth_types():
return [loader for loader in loading.get_available_plugin_loaders()]
def get_loader(auth_type):
if auth_type not in list_auth_types():
raise ValueError(
"'auth_type' has to be one of %s (received %s)" % (
list_auth_types(), auth_type
)
)
return loading.get_plugin_loader(auth_type)
def extract_loader_kwargs(loader, **kwargs):
"""Get keystoneauth1.loading.get_plugin_loader's auth kwargs
Return the specific auth kwargs and remaining kwargs
"""
loader_keys = [option.dest for option in loader.get_options()]
loader_kwargs = {
key: kwargs[key] for key in loader_keys if key in kwargs
}
remaining_kwargs = {
key: kwargs[key] for key in kwargs if key not in loader_keys
}
return loader_kwargs, remaining_kwargs
def get_openstack_config_with_envvars(
parser, config=None, load_envvars=False, load_yaml_config=False
):
"""Add openstack_options to argparse.ArgumentParser
:param argparse.ArgumentParser parser: argparse.ArgumentParser instance
or None.
:param bool load_envvars:
Whether or not to load config settings from environment variables.
Defaults to True.
:returns: OpenStackConfig instance
"""
if config is None:
# If we use `openstack.config.loader.OpenStackConfig` instead of
# `os_client_config.config.OpenStackConfig` and then instantiate a
# `shade.openstackcloud.OpenStackCloud` instance as `cloud`, when
# trying to access the `cloud.keystone_client` attribute (or any other
# `*_client attribute`), we get a
# "TypeError: 'NoneType' object is not callable" exception
# This is why we use `os_client_config.config.OpenStackConfig`: it's to
# have all `shade.openstackcloud.OpenStackCloud` attributes working
# properly. Whether we use them or not, whether these attributes are
# deprecated or not, we do not want a 'broken' shade instance in case
# third party managers would use them.
config = OpenStackConfig(
load_envvars=load_envvars, load_yaml_config=load_yaml_config
)
if parser:
if load_envvars or load_yaml_config:
config.register_argparse_arguments(parser, sys.argv)
else:
config.register_argparse_arguments(parser, [])
return config
def get_openstack_cli_arguments(
parser=None, load_envvars=True, load_yaml_config=True,
renamed_args=False
):
if parser is None:
parser = argparse.ArgumentParser()
get_openstack_config_with_envvars(
parser, load_envvars=load_envvars, load_yaml_config=load_yaml_config
)
known_args, unknown_args = parser.parse_known_args()
if renamed_args:
known_args = utils.rename_os_options(known_args, clean=False)
return known_args, unknown_args
def get_openstack_envvars_as_kwargs(
with_args=False, parser=None, load_envvars=True, load_yaml_config=True
):
"""Get openstack environment variables"""
known_args, unknown_args = get_openstack_cli_arguments(
parser=parser, load_envvars=load_envvars,
load_yaml_config=load_yaml_config
)
kwargs = utils.rename_os_kwargs(vars(known_args), clean=True)
if with_args:
return known_args, unknown_args, kwargs
return kwargs
def get_keystoneauth1_session(
load_envvars=False, load_yaml_config=False, **kwargs
):
if load_envvars:
kwargs.update(
get_openstack_envvars_as_kwargs(
load_envvars=load_envvars, load_yaml_config=load_yaml_config
)
)
auth_type = kwargs.get('auth_type', 'password')
loader = get_loader(auth_type)
loader_kwargs, _ = extract_loader_kwargs(loader, **kwargs)
auth = loader.load_from_options(**loader_kwargs)
return keystone_session.Session(auth=auth)
get_keystone_session = get_keystoneauth1_session
def get_openstack_config(
parser_or_options=None, load_envvars=False, load_yaml_config=False,
**kwargs
):
"""Same as os_client_config.get_config with less errors.
Indeed, if we source OS_* variable environments, and one calls:
os_client_config.get_config(
load_envvars=False, load_yaml_config=False, **kwargs
)
we get this kind of error:
ConfigException: Region fr1 is not a valid region name for cloud
envvars. Valid choices are fr0. Please note that region names are case
sensitive.
It seems like os_client_config.get_config fails to NOT handle envvars.
Also, os_client_config.get_config saves the config in a global variable...
This is absolutely not thread safe...
"""
parsed_options = None
parser = None
if isinstance(parser_or_options, argparse.ArgumentParser):
parser = parser_or_options
config = get_openstack_config_with_envvars(
parser=parser, load_envvars=load_envvars,
load_yaml_config=load_yaml_config
)
if parser_or_options is not None:
if isinstance(parser_or_options, argparse.Namespace):
parsed_options = parser_or_options
elif isinstance(parser_or_options, dict):
parsed_options = utils.dict_to_options(parser_or_options)
elif isinstance(parser_or_options, argparse.ArgumentParser):
if load_envvars or load_yaml_config:
parsed_options, _ = parser_or_options.parse_known_args(
sys.argv)
else:
parsed_options, _ = parser_or_options.parse_known_args([])
else:
raise AttributeError(
"'parser_options' has to be an 'argparse.ArgumentParser' or "
"'argparse.Namespace' instance or dict or None. "
"Received '%s'" % type(parser_or_options)
)
return config.get_one(
options=parsed_options,
load_yaml_config=load_yaml_config,
load_envvars=load_envvars,
**kwargs
)
def get_openstack_sdk_connection(
parser_or_options=None, load_envvars=False, load_yaml_config=False,
cloud_config=None, session=None,
**kwargs
):
if session is not None:
return os_connection.Connection(session=session, **kwargs)
if cloud_config is None:
# we could return
# `openstack.connect(load_envvars=load_envvars, load_yaml_config=load_yaml_config, **kwargs)` # noqa
# but by doing so magic things are lacking in the config and we have
# random failing methods on the instance. See comments in
# get_openstack_config_with_envvars for more information.
cloud_config = get_openstack_config(
parser_or_options=parser_or_options, load_envvars=load_envvars,
load_yaml_config=load_yaml_config,
**kwargs
)
if isinstance(parser_or_options, dict):
parser_or_options = utils.dict_to_options(parser_or_options)
return os_connection.from_config(
cloud_config=cloud_config, options=parser_or_options
)
def get_shade(
parser_or_options=None, cloud_config=None, connection=None,
load_envvars=False, load_yaml_config=False,
**kwargs
):
"""Get shade instance
You can use an `argparse.ArgumentParser` or `argparse.Namespace` instance
with `load_envvars` and/or `load_yaml_config` set to True,
Or you kan use kwargs to authenticate with `load_envvars` AND
`load_yaml_config` set to False:
cloud = get_shade(
auth_type='password',
auth_url='https://identity.fr1.cloudwatt.com/v2.0',
interface='public',
password='YourPassword',
project_id='Your ProjectID,
project_name='YourProjectName,
region_name='YourRegionName',
username='YourUserName'
)
You can also use a token instead of password with kwargs, If so, use
`auth_type='token'`.
"""
if cloud_config is not None:
return OpenStackCloud(cloud_config=cloud_config, **kwargs)
elif connection is not None:
return OpenStackCloud(cloud_config=connection.config, **kwargs)
else:
return OpenStackCloud(
cloud_config=get_openstack_config(
parser_or_options=parser_or_options, load_envvars=load_envvars,
load_yaml_config=load_yaml_config,
**kwargs
)
)

View File

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
try:
from unittest import mock # Python 3.3+
except ImportError:
import mock # noqa: Python 2.7
try:
import unittest2 as unittest # Python 2.7
except ImportError:
import unittest # noqa

View File

@ -1,53 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2011 OpenStack Foundation
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import fixtures
import testtools
_TRUE_VALUES = ('True', 'true', '1', 'yes')
class TestCase(testtools.TestCase):
"""Test case base class for all unit tests."""
def setUp(self):
"""Run before each test method to initialize test environment."""
super(TestCase, self).setUp()
test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
try:
test_timeout = int(test_timeout)
except ValueError:
# If timeout value is invalid do not set a timeout.
test_timeout = 0
if test_timeout > 0:
self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
self.useFixture(fixtures.NestedTempfile())
self.useFixture(fixtures.TempHomeDir())
if os.environ.get('OS_STDOUT_CAPTURE') in _TRUE_VALUES:
stdout = self.useFixture(fixtures.StringStream('stdout')).stream
self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
if os.environ.get('OS_STDERR_CAPTURE') in _TRUE_VALUES:
stderr = self.useFixture(fixtures.StringStream('stderr')).stream
self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
self.log_fixture = self.useFixture(fixtures.FakeLogger())

30
flameclient/tests/fixtures/__init__.py vendored Normal file
View File

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
FIXTURES_DIR = os.path.realpath(
os.path.dirname(__file__)
)

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from flameclient.utils import load_resource_modules
FIXTURES = load_resource_modules(__file__)

View File

@ -0,0 +1,432 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
NAME = 'compute.flavors'
FIXTURES = [{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 20,
'id': '16',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/16',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/16',
'rel': 'bookmark'}],
'name': 't1.cw.tiny',
'os-flavor-access:is_public': True,
'ram': 629,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 1},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '17',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/17',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/17',
'rel': 'bookmark'}],
'name': 's1.cw.small-1',
'os-flavor-access:is_public': True,
'ram': 1792,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 1},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '18',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/18',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/18',
'rel': 'bookmark'}],
'name': 'n1.cw.highcpu-2',
'os-flavor-access:is_public': True,
'ram': 4000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 2},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '19',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/19',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/19',
'rel': 'bookmark'}],
'name': 'n1.cw.highcpu-4',
'os-flavor-access:is_public': True,
'ram': 8000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 4},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '20',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/20',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/20',
'rel': 'bookmark'}],
'name': 'n1.cw.highcpu-8',
'os-flavor-access:is_public': True,
'ram': 16000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 8},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '21',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/21',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/21',
'rel': 'bookmark'}],
'name': 'n1.cw.standard-1',
'os-flavor-access:is_public': True,
'ram': 4000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 1},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '22',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/22',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/22',
'rel': 'bookmark'}],
'name': 'n1.cw.standard-2',
'os-flavor-access:is_public': True,
'ram': 8000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 2},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '23',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/23',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/23',
'rel': 'bookmark'}],
'name': 'n1.cw.standard-4',
'os-flavor-access:is_public': True,
'ram': 16000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 4},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '24',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/24',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/24',
'rel': 'bookmark'}],
'name': 'n1.cw.standard-8',
'os-flavor-access:is_public': True,
'ram': 32000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 8},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '25',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/25',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/25',
'rel': 'bookmark'}],
'name': 'n1.cw.standard-12',
'os-flavor-access:is_public': True,
'ram': 48000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 12},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '26',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/26',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/26',
'rel': 'bookmark'}],
'name': 'n1.cw.standard-16',
'os-flavor-access:is_public': True,
'ram': 64000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 16},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '28',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/28',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/28',
'rel': 'bookmark'}],
'name': 'n1.cw.highmem-2',
'os-flavor-access:is_public': True,
'ram': 13312,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 2},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '29',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/29',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/29',
'rel': 'bookmark'}],
'name': 'n1.cw.highmem-4',
'os-flavor-access:is_public': True,
'ram': 26624,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 4},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '30',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/30',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/30',
'rel': 'bookmark'}],
'name': 'n1.cw.highmem-8',
'os-flavor-access:is_public': True,
'ram': 53248,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 8},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '31',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/31',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/31',
'rel': 'bookmark'}],
'name': 'n1.cw.highmem-12',
'os-flavor-access:is_public': True,
'ram': 79872,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 12},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '38',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/38',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/38',
'rel': 'bookmark'}],
'name': 'n2.cw.highmem-2',
'os-flavor-access:is_public': True,
'ram': 13312,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 2},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 50,
'description': None,
'disk': 50,
'id': '39',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/39',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/39',
'rel': 'bookmark'}],
'name': 'n2.cw.highmem-4',
'os-flavor-access:is_public': True,
'ram': 26624,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 4},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 100,
'description': None,
'disk': 50,
'id': '40',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/40',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/40',
'rel': 'bookmark'}],
'name': 'n2.cw.highmem-8',
'os-flavor-access:is_public': True,
'ram': 53248,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 8},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 300,
'description': None,
'disk': 50,
'id': '41',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/41',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/41',
'rel': 'bookmark'}],
'name': 'n2.cw.highmem-16',
'os-flavor-access:is_public': True,
'ram': 104000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 16},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '42',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/42',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/42',
'rel': 'bookmark'}],
'name': 'n2.cw.standard-1',
'os-flavor-access:is_public': True,
'ram': 4000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 1},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '43',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/43',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/43',
'rel': 'bookmark'}],
'name': 'n2.cw.standard-2',
'os-flavor-access:is_public': True,
'ram': 8000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 2},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '44',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/44',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/44',
'rel': 'bookmark'}],
'name': 'n2.cw.standard-4',
'os-flavor-access:is_public': True,
'ram': 16000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 4},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '45',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/45',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/45',
'rel': 'bookmark'}],
'name': 'n2.cw.standard-8',
'os-flavor-access:is_public': True,
'ram': 32000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 8},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 0,
'description': None,
'disk': 50,
'id': '46',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/46',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/46',
'rel': 'bookmark'}],
'name': 'n2.cw.standard-16',
'os-flavor-access:is_public': True,
'ram': 64000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 16},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 400,
'description': None,
'disk': 50,
'id': '53',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/53',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/53',
'rel': 'bookmark'}],
'name': 'i2.cw.largessd-4',
'os-flavor-access:is_public': True,
'ram': 32000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 4},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 850,
'description': None,
'disk': 50,
'id': '54',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/54',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/54',
'rel': 'bookmark'}],
'name': 'i2.cw.largessd-8',
'os-flavor-access:is_public': True,
'ram': 64000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 8},
{'OS-FLV-DISABLED:disabled': False,
'OS-FLV-EXT-DATA:ephemeral': 1750,
'description': None,
'disk': 50,
'id': '55',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/flavors/55',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/55',
'rel': 'bookmark'}],
'name': 'i2.cw.largessd-16',
'os-flavor-access:is_public': True,
'ram': 128000,
'rxtx_factor': 1.0,
'swap': '',
'vcpus': 16}]

View File

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
NAME = 'network.ips'
FIXTURES = [{'created_at': None,
'description': None,
'dns_domain': None,
'dns_name': None,
'fixed_ip_address': '192.168.0.8',
'floating_ip_address': '84.39.32.171',
'floating_network_id': '6ea98324-0f14-49f6-97c0-885d1b8dc517',
'id': 'e6f50641-6c7e-468c-9623-1fd5ee2c1ebc',
'name': '84.39.32.171',
'port_details': None,
'port_id': None,
'qos_policy_id': None,
'revision_number': None,
'router_id': None,
'status': 'ACTIVE',
'subnet_id': None,
'tags': [],
'tenant_id': '9824a7403a1b411d8d207d26218597ce',
'updated_at': None}]

View File

@ -0,0 +1,201 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
NAME = 'image.images'
FIXTURES = [{'architecture': None,
'auto_disk_config': None,
'checksum': '57baccdf32b77ff7939d429b842d4440',
'container_format': 'bare',
'created_at': '2017-06-20T14:53:41Z',
'direct_url': None,
'disk_format': 'qcow2',
'file': '/v2/images/8254d703-e46f-4101-88ff-6f40bf7df51a/file',
'hw_boot_menu': None,
'hw_cpu_cores': None,
'hw_cpu_sockets': None,
'hw_cpu_threads': None,
'hw_disk_bus': None,
'hw_machine_type': None,
'hw_qemu_guest_agent': None,
'hw_rng_model': 'virtio',
'hw_scsi_model': None,
'hw_serial_port_count': None,
'hw_video_model': None,
'hw_video_ram': None,
'hw_vif_model': None,
'hw_vif_multiqueue_enabled': None,
'hw_watchdog_action': None,
'hypervisor-type': None,
'id': '8254d703-e46f-4101-88ff-6f40bf7df51a',
'img_config_drive': None,
'instance_type_rxtx_factor': None,
'instance_uuid': None,
'kernel_id': None,
'locations': None,
'metadata': None,
'min_disk': 50,
'min_ram': 2048,
'name': 'Windows Server 2012 R2 Standard FR',
'os_admin_user': None,
'os_command_line': None,
'os_distro': None,
'os_require_quiesce': None,
'os_secure_boot': None,
'os_type': None,
'os_version': None,
'owner': '3ab52a7f1b824983bf418df633d9d88d',
'path': None,
'properties': None,
'protected': False,
'ramdisk_id': None,
'size': 7976386560,
'status': 'active',
'store': None,
'tags': [],
'updated_at': '2017-06-20T14:56:43Z',
'url': None,
'value': None,
'virtual_size': None,
'visibility': 'public',
'vm_mode': None,
'vmware_adaptertype': None,
'vmware_ostype': None},
{'architecture': None,
'auto_disk_config': None,
'checksum': '8ec802fe753dfe8e226645a2e0106bf7',
'container_format': 'bare',
'created_at': '2017-03-23T15:46:41Z',
'direct_url': None,
'disk_format': 'qcow2',
'file': '/v2/images/70a9c910-dd99-4065-bce9-11e89bc479fe/file',
'hw_boot_menu': None,
'hw_cpu_cores': None,
'hw_cpu_sockets': None,
'hw_cpu_threads': None,
'hw_disk_bus': None,
'hw_machine_type': None,
'hw_qemu_guest_agent': None,
'hw_rng_model': 'virtio',
'hw_scsi_model': None,
'hw_serial_port_count': None,
'hw_video_model': None,
'hw_video_ram': None,
'hw_vif_model': None,
'hw_vif_multiqueue_enabled': None,
'hw_watchdog_action': None,
'hypervisor-type': None,
'id': '70a9c910-dd99-4065-bce9-11e89bc479fe',
'img_config_drive': None,
'instance_type_rxtx_factor': None,
'instance_uuid': None,
'kernel_id': None,
'locations': None,
'metadata': None,
'min_disk': 20,
'min_ram': 0,
'name': 'Ubuntu 14.04',
'os_admin_user': None,
'os_command_line': None,
'os_distro': None,
'os_require_quiesce': None,
'os_secure_boot': None,
'os_type': None,
'os_version': None,
'owner': '3ab52a7f1b824983bf418df633d9d88d',
'path': None,
'properties': None,
'protected': False,
'ramdisk_id': None,
'size': 1009057792,
'status': 'active',
'store': None,
'tags': [],
'updated_at': '2017-12-26T10:33:50Z',
'url': None,
'value': None,
'virtual_size': None,
'visibility': 'public',
'vm_mode': None,
'vmware_adaptertype': None,
'vmware_ostype': None},
{'architecture': None,
'auto_disk_config': None,
'checksum': '12872d8897d2eabfceac8b4627ff88f0',
'container_format': 'bare',
'created_at': '2016-06-07T07:36:26Z',
'direct_url': None,
'disk_format': 'qcow2',
'file': '/v2/images/dd7d4b21-79b8-42f0-8464-0d1a5274c638/file',
'hw_boot_menu': None,
'hw_cpu_cores': None,
'hw_cpu_sockets': None,
'hw_cpu_threads': None,
'hw_disk_bus': None,
'hw_machine_type': None,
'hw_qemu_guest_agent': None,
'hw_rng_model': None,
'hw_scsi_model': None,
'hw_serial_port_count': None,
'hw_video_model': None,
'hw_video_ram': None,
'hw_vif_model': None,
'hw_vif_multiqueue_enabled': None,
'hw_watchdog_action': None,
'hypervisor-type': None,
'id': 'dd7d4b21-79b8-42f0-8464-0d1a5274c638',
'img_config_drive': None,
'instance_type_rxtx_factor': None,
'instance_uuid': None,
'kernel_id': None,
'locations': None,
'metadata': None,
'min_disk': 0,
'min_ram': 0,
'name': 'tempest-ubuntu-do-not-erase',
'os_admin_user': None,
'os_command_line': None,
'os_distro': None,
'os_require_quiesce': None,
'os_secure_boot': None,
'os_type': None,
'os_version': None,
'owner': '3ab52a7f1b824983bf418df633d9d88d',
'path': None,
'properties': None,
'protected': False,
'ramdisk_id': None,
'size': 261488640,
'status': 'active',
'store': None,
'tags': [],
'updated_at': '2016-06-07T07:36:31Z',
'url': None,
'value': None,
'virtual_size': None,
'visibility': 'private',
'vm_mode': None,
'vmware_adaptertype': None,
'vmware_ostype': None}]

View File

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
NAME = 'compute.keypairs'
FIXTURES = [{'fingerprint': '01:99:63:d2:96:d4:23:25:2f:d5:c9:e4:2f:30:d2:3f',
'id': 'tellurium-key',
'name': 'tellurium-key',
'private_key': None,
'public_key': 'ssh-rsa '
'AAAAB3NzaC1yc2EAAAADAQABAAABAQCwp5kVZ+3baPZllNXZDG2mivd5nJ5wWY6Jj/WV6NlO6cUiaH5om6itU3lyJxtAbLgbbvY2FjMg1PI2JI3EHx0OSPEbDdeNsQGi31qyuiB1S5p6TreI0Dfy0tywJ9G2CURjkuJnC8SvnMfVYLMFBvx7p8RzxSdDm/zrmc4KY3ktdfYQDNtEiH2jucUUiY0ipVkDNhhv03+5C9cnpaIcVDBkddE/KEME8NIIh7s6aYCXJEWJx85nOVVRD5qK7ouV6FcGVn6zqWRD3jn0iSxcFwiKx7p6M77PmJAY4gBIpWQmutok6T4ZrXxa7jE4dybwo5e8dyvGyc7WWGqXcmcinUWr '
'Generated-by-Nova'},
{'fingerprint': '76:e2:94:d2:13:c0:72:49:43:a7:58:f5:92:2f:e9:62',
'id': 'tellurium-key-phrase',
'name': 'tellurium-key-phrase',
'private_key': None,
'public_key': 'ssh-rsa '
'AAAAB3NzaC1yc2EAAAADAQABAAABAQCaYaTC6KCC2GURFMpRQiRq8Og10PcBbVnrQqluzi2E5vAqTHAegzivmGB8h2xrjXUfbKV2bE8kz2ZE66sq1yLO/jZ+rkOA+zLnOxNwkk3gQq57b26ZegPL2tTgottJTPGyPz6v2+LLtDf/+xTJMjkPKcMWylu12Js0XKVkdY35fwN7fRvA4xghtGu1GcmS2XFDMmeLDrG1KNPbBLj5cGoD723Ho7ZhAjLThoY/xMN2OYsSNzrg3S00QngZMzQvJf0ETrB3GtITk5FUs54qMRfiyUC72lw0gLTIJK8rs7fX/yeO+467afRt2xwHN50rEWJPeTVYWdlH/msh7NQ3LZ8/ '
'root@9b638ff21113'}]

View File

@ -0,0 +1,103 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
NAME = 'network.networks'
FIXTURES = [{'admin_state_up': True,
'availability_zone_hints': None,
'availability_zones': None,
'created_at': None,
'description': None,
'dns_domain': None,
'id': '6ea98324-0f14-49f6-97c0-885d1b8dc517',
'ipv4_address_scope': None,
'ipv6_address_scope': None,
'is_default': None,
'mtu': None,
'name': 'public',
'port_security_enabled': False,
'project_id': None,
'provider:network_type': None,
'provider:physical_network': None,
'provider:segmentation_id': None,
'qos_policy_id': None,
'revision_number': None,
'router:external': True,
'segments': None,
'shared': False,
'status': 'ACTIVE',
'subnets': ['7ce9190e-397e-4523-bb9a-942a338555c8',
'16af2b37-a739-474a-b64d-ad5b515b24d7',
'85bc9804-bde9-4fc3-ac28-138bbed0be47',
'12469c2c-6902-460a-ad54-9252ca791d55',
'84095c22-48af-4cf5-b03d-0c745bfa1586',
'12f473cc-a01e-4f70-86d7-c1ae05058545',
'02efcad7-4744-41c4-8429-332828309d4f',
'aa8fc58b-5db8-45ba-a354-180e5e2ae84b',
'6ac09891-9788-46b7-8165-9d2c9cefa483',
'38d8fc45-2698-4ffc-9beb-59331e3ef68d',
'aded311e-5766-4adc-b6d8-64dd5c93a8c3',
'b7f41f14-35a2-4bf4-a2b3-60c2200c0fb5',
'65d8722e-09f6-4b39-a693-e61018867377',
'c5387c58-542c-4cfd-8338-afcbd37e4546',
'baa6798a-0eba-417e-b294-31fbf1c4a7c5',
'3c8fde67-c209-4716-a515-5777667a4835',
'bf806833-7f33-4254-865f-8a874d36d17c',
'6736cae7-7501-4e10-af94-bb06acaf5de6',
'7b2e3c57-e8cc-47ac-a008-e0ee2db5e567',
'85342bf1-7e69-4468-b269-8435c4991773',
'6e289f4e-ec6a-4fd7-9bbc-94c3f17e57a8',
'de042fd3-acf3-487f-819f-7b18617728ec',
'da35a785-99ea-4d54-b056-0c20feca1125'],
'tags': [],
'updated_at': None,
'vlan_transparent': None},
{'admin_state_up': True,
'availability_zone_hints': None,
'availability_zones': None,
'created_at': None,
'description': None,
'dns_domain': None,
'id': 'f054013d-7052-4708-9c72-2948a329fac3',
'ipv4_address_scope': None,
'ipv6_address_scope': None,
'is_default': None,
'mtu': None,
'name': 'tellurium_net',
'port_security_enabled': False,
'project_id': None,
'provider:network_type': None,
'provider:physical_network': None,
'provider:segmentation_id': None,
'qos_policy_id': None,
'revision_number': None,
'router:external': False,
'segments': None,
'shared': False,
'status': 'ACTIVE',
'subnets': ['541f0782-587f-428b-bd79-ca227a66973b'],
'tags': [],
'updated_at': None,
'vlan_transparent': None}]

View File

@ -0,0 +1,183 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
NAME = 'network.ports'
FIXTURES = [{'admin_state_up': True,
'allowed_address_pairs': None,
'binding:host_id': None,
'binding:profile': None,
'binding:vif_details': None,
'binding:vif_type': None,
'binding:vnic_type': 'normal',
'created_at': None,
'data_plane_status': None,
'description': None,
'device_id': '2cf8db13-312a-4307-ba38-43b727ebcce6',
'device_owner': 'compute:None',
'dns_assignment': None,
'dns_domain': None,
'dns_name': None,
'extra_dhcp_opts': None,
'fixed_ips': [{'ip_address': '192.168.0.3',
'subnet_id': '541f0782-587f-428b-bd79-ca227a66973b'}],
'id': '3decf6f8-2591-4ab0-af19-00f6ce6a0cf5',
'mac_address': '02:3d:ec:f6:f8:25',
'name': 'Tellurium_Fixtures-windows_port-hxvnswbzi56x',
'network_id': 'f054013d-7052-4708-9c72-2948a329fac3',
'port_security_enabled': False,
'qos_policy_id': None,
'revision_number': None,
'security_groups': ['9ffd2654-7ca6-48ae-852d-6503d5ce4a60'],
'status': 'ACTIVE',
'tags': [],
'tenant_id': '9824a7403a1b411d8d207d26218597ce',
'trunk_details': None,
'updated_at': None},
{'admin_state_up': True,
'allowed_address_pairs': None,
'binding:host_id': None,
'binding:profile': None,
'binding:vif_details': None,
'binding:vif_type': None,
'binding:vnic_type': 'normal',
'created_at': None,
'data_plane_status': None,
'description': None,
'device_id': '4add78fa-93df-4550-aaf7-aba2239ba00a',
'device_owner': 'compute:None',
'dns_assignment': None,
'dns_domain': None,
'dns_name': None,
'extra_dhcp_opts': None,
'fixed_ips': [{'ip_address': '192.168.0.4',
'subnet_id': '541f0782-587f-428b-bd79-ca227a66973b'}],
'id': '206df716-da1a-4a50-ba1a-74f42a007dd0',
'mac_address': '02:20:6d:f7:16:da',
'name': 'Tellurium_Fixtures-instance_port-fmln6fjl4yvo',
'network_id': 'f054013d-7052-4708-9c72-2948a329fac3',
'port_security_enabled': False,
'qos_policy_id': None,
'revision_number': None,
'security_groups': ['9ffd2654-7ca6-48ae-852d-6503d5ce4a60'],
'status': 'ACTIVE',
'tags': [],
'tenant_id': '9824a7403a1b411d8d207d26218597ce',
'trunk_details': None,
'updated_at': None},
{'admin_state_up': True,
'allowed_address_pairs': None,
'binding:host_id': None,
'binding:profile': None,
'binding:vif_details': None,
'binding:vif_type': None,
'binding:vnic_type': 'normal',
'created_at': None,
'data_plane_status': None,
'description': None,
'device_id': 'b6316031-e629-48b8-aac5-3f1b21ffe0f3',
'device_owner': 'compute:None',
'dns_assignment': None,
'dns_domain': None,
'dns_name': None,
'extra_dhcp_opts': None,
'fixed_ips': [{'ip_address': '192.168.0.5',
'subnet_id': '541f0782-587f-428b-bd79-ca227a66973b'}],
'id': 'cb336b60-f298-4a51-9443-89880e3cdb51',
'mac_address': '02:cb:33:6b:60:f2',
'name': 'Tellurium_Fixtures-windows_ssh_pass_port-4zlwanbetsg2',
'network_id': 'f054013d-7052-4708-9c72-2948a329fac3',
'port_security_enabled': False,
'qos_policy_id': None,
'revision_number': None,
'security_groups': ['9ffd2654-7ca6-48ae-852d-6503d5ce4a60'],
'status': 'ACTIVE',
'tags': [],
'tenant_id': '9824a7403a1b411d8d207d26218597ce',
'trunk_details': None,
'updated_at': None},
{'admin_state_up': True,
'allowed_address_pairs': None,
'binding:host_id': None,
'binding:profile': None,
'binding:vif_details': None,
'binding:vif_type': None,
'binding:vnic_type': 'normal',
'created_at': None,
'data_plane_status': None,
'description': None,
'device_id': 'ebc6dd0c-a276-4368-8084-bd37c587cc24',
'device_owner': 'network:router_interface',
'dns_assignment': None,
'dns_domain': None,
'dns_name': None,
'extra_dhcp_opts': None,
'fixed_ips': [{'ip_address': '192.168.0.1',
'subnet_id': '541f0782-587f-428b-bd79-ca227a66973b'}],
'id': 'e3b88764-3c46-44aa-ae26-fa6ec2df93bd',
'mac_address': '02:e3:b8:87:64:3c',
'name': 'e3b88764-3c46-44aa-ae26-fa6ec2df93bd',
'network_id': 'f054013d-7052-4708-9c72-2948a329fac3',
'port_security_enabled': False,
'qos_policy_id': None,
'revision_number': None,
'security_groups': ['9ffd2654-7ca6-48ae-852d-6503d5ce4a60'],
'status': 'ACTIVE',
'tags': [],
'tenant_id': '9824a7403a1b411d8d207d26218597ce',
'trunk_details': None,
'updated_at': None},
{'admin_state_up': True,
'allowed_address_pairs': None,
'binding:host_id': None,
'binding:profile': None,
'binding:vif_details': None,
'binding:vif_type': None,
'binding:vnic_type': 'normal',
'created_at': None,
'data_plane_status': None,
'description': None,
'device_id': '99c42e21-5099-4903-9121-063925aad299',
'device_owner': 'compute:nova',
'dns_assignment': None,
'dns_domain': None,
'dns_name': None,
'extra_dhcp_opts': None,
'fixed_ips': [{'ip_address': '192.168.0.6',
'subnet_id': '541f0782-587f-428b-bd79-ca227a66973b'}],
'id': '4f413432-b499-4fcc-a81a-1ee0d8bc16b7',
'mac_address': '02:4f:41:34:32:b4',
'name': '4f413432-b499-4fcc-a81a-1ee0d8bc16b7',
'network_id': 'f054013d-7052-4708-9c72-2948a329fac3',
'port_security_enabled': False,
'qos_policy_id': None,
'revision_number': None,
'security_groups': ['156799a3-565e-48b3-938c-f95f09093c66',
'9ffd2654-7ca6-48ae-852d-6503d5ce4a60'],
'status': 'ACTIVE',
'tags': [],
'tenant_id': '9824a7403a1b411d8d207d26218597ce',
'trunk_details': None,
'updated_at': None}]

View File

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
NAME = 'network.routers'
FIXTURES = [{'admin_state_up': True,
'availability_zone_hints': None,
'availability_zones': None,
'created_at': None,
'description': None,
'distributed': None,
'external_gateway_info': {'enable_snat': True,
'network_id': '6ea98324-0f14-49f6-97c0-885d1b8dc517'},
'flavor_id': None,
'ha': None,
'id': 'ebc6dd0c-a276-4368-8084-bd37c587cc24',
'name': 'tellurium_router',
'revision': None,
'routes': None,
'status': 'ACTIVE',
'tags': [],
'tenant_id': '9824a7403a1b411d8d207d26218597ce',
'updated_at': None}]

View File

@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
NAME = 'network.security_groups'
FIXTURES = [{'created_at': None,
'description': None,
'id': '9ffd2654-7ca6-48ae-852d-6503d5ce4a60',
'name': 'default',
'revision_number': None,
'security_group_rules': [{'direction': 'ingress',
'ethertype': 'IPv4',
'id': 'ec72eaca-22e2-47b6-b1a5-2fe6f8129e47',
'port_range_max': None,
'port_range_min': None,
'protocol': None,
'remote_group_id': '9ffd2654-7ca6-48ae-852d-6503d5ce4a60',
'remote_ip_prefix': None,
'security_group_id': '9ffd2654-7ca6-48ae-852d-6503d5ce4a60',
'tenant_id': '9824a7403a1b411d8d207d26218597ce'},
{'direction': 'ingress',
'ethertype': 'IPv6',
'id': 'd5720f56-e7ce-4c7f-982d-a70d76c37b11',
'port_range_max': None,
'port_range_min': None,
'protocol': None,
'remote_group_id': '9ffd2654-7ca6-48ae-852d-6503d5ce4a60',
'remote_ip_prefix': None,
'security_group_id': '9ffd2654-7ca6-48ae-852d-6503d5ce4a60',
'tenant_id': '9824a7403a1b411d8d207d26218597ce'},
{'direction': 'egress',
'ethertype': 'IPv4',
'id': 'd05bc784-b34c-4e0c-a2ea-7b10992caa1e',
'port_range_max': None,
'port_range_min': None,
'protocol': None,
'remote_group_id': None,
'remote_ip_prefix': '0.0.0.0/0',
'security_group_id': '9ffd2654-7ca6-48ae-852d-6503d5ce4a60',
'tenant_id': '9824a7403a1b411d8d207d26218597ce'},
{'direction': 'egress',
'ethertype': 'IPv6',
'id': 'a6abbc8e-92c2-42f6-9ac9-a04544588739',
'port_range_max': None,
'port_range_min': None,
'protocol': None,
'remote_group_id': None,
'remote_ip_prefix': '::/0',
'security_group_id': '9ffd2654-7ca6-48ae-852d-6503d5ce4a60',
'tenant_id': '9824a7403a1b411d8d207d26218597ce'}],
'tags': [],
'tenant_id': '9824a7403a1b411d8d207d26218597ce',
'updated_at': None},
{'created_at': None,
'description': '',
'id': '156799a3-565e-48b3-938c-f95f09093c66',
'name': 'http',
'revision_number': None,
'security_group_rules': [{'direction': 'egress',
'ethertype': 'IPv4',
'id': '80508f7d-b893-4bcb-bddc-51a946634492',
'port_range_max': None,
'port_range_min': None,
'protocol': None,
'remote_group_id': None,
'remote_ip_prefix': '0.0.0.0/0',
'security_group_id': '156799a3-565e-48b3-938c-f95f09093c66',
'tenant_id': '9824a7403a1b411d8d207d26218597ce'},
{'direction': 'egress',
'ethertype': 'IPv6',
'id': 'eab50b80-bc3c-484c-a3c8-fd6cf5ee9c50',
'port_range_max': None,
'port_range_min': None,
'protocol': None,
'remote_group_id': None,
'remote_ip_prefix': None,
'security_group_id': '156799a3-565e-48b3-938c-f95f09093c66',
'tenant_id': '9824a7403a1b411d8d207d26218597ce'},
{'direction': 'ingress',
'ethertype': 'IPv4',
'id': 'a6ed0dc8-ee29-462e-84a0-961675e08c4a',
'port_range_max': 80,
'port_range_min': 80,
'protocol': 'tcp',
'remote_group_id': None,
'remote_ip_prefix': '0.0.0.0/0',
'security_group_id': '156799a3-565e-48b3-938c-f95f09093c66',
'tenant_id': '9824a7403a1b411d8d207d26218597ce'}],
'tags': [],
'tenant_id': '9824a7403a1b411d8d207d26218597ce',
'updated_at': None}]

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
NAME = 'compute.server_groups'
FIXTURES = []

View File

@ -0,0 +1,213 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
NAME = 'compute.servers'
FIXTURES = [{'OS-DCF:diskConfig': 'AUTO',
'OS-EXT-AZ:availability_zone': 'nova',
'OS-EXT-SRV-ATTR:hypervisor_hostname': None,
'OS-EXT-SRV-ATTR:instance_name': None,
'OS-EXT-SRV-ATTR:user_data': None,
'OS-EXT-STS:power_state': 1,
'OS-EXT-STS:task_state': None,
'OS-EXT-STS:vm_state': 'active',
'OS-SCH-HNT:scheduler_hints': None,
'OS-SRV-USG:launched_at': '2018-11-09T13:39:42.000000',
'OS-SRV-USG:terminated_at': None,
'accessIPv4': '',
'accessIPv6': '',
'addresses': {'tellurium_net': [{'OS-EXT-IPS-MAC:mac_addr': '02:4f:41:34:32:b4',
'OS-EXT-IPS:type': 'fixed',
'addr': '192.168.0.6',
'version': 4}]},
'adminPass': None,
'block_device_mapping_v2': None,
'config_drive': '',
'created': '2018-11-09T13:39:38Z',
'flavor': {'id': '17',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/17',
'rel': 'bookmark'}]},
'flavorRef': None,
'hostId': '84246cf2f8ac920628901e7a84a054b45f246f2d77f6df3f6c30cc43',
'id': '99c42e21-5099-4903-9121-063925aad299',
'image': {},
'imageRef': None,
'key_name': 'tellurium-key',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/servers/99c42e21-5099-4903-9121-063925aad299',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/servers/99c42e21-5099-4903-9121-063925aad299',
'rel': 'bookmark'}],
'metadata': {},
'name': 'Ubuntu 14.04',
'networks': None,
'os-extended-volumes:volumes_attached': [{'id': '8ca10346-fe4c-4e68-9a18-98df875d1ecc'}],
'personality': None,
'progress': 0,
'security_groups': [{'name': 'http'}, {'name': 'default'}],
'status': 'ACTIVE',
'tenant_id': '9824a7403a1b411d8d207d26218597ce',
'updated': '2018-11-09T13:39:42Z',
'user_id': '7f71ea7b8abd41e5a1f303f4a1bc16b9'},
{'OS-DCF:diskConfig': 'MANUAL',
'OS-EXT-AZ:availability_zone': 'nova',
'OS-EXT-SRV-ATTR:hypervisor_hostname': None,
'OS-EXT-SRV-ATTR:instance_name': None,
'OS-EXT-SRV-ATTR:user_data': None,
'OS-EXT-STS:power_state': 1,
'OS-EXT-STS:task_state': None,
'OS-EXT-STS:vm_state': 'active',
'OS-SCH-HNT:scheduler_hints': None,
'OS-SRV-USG:launched_at': '2018-11-09T04:54:35.000000',
'OS-SRV-USG:terminated_at': None,
'accessIPv4': '',
'accessIPv6': '',
'addresses': {'tellurium_net': [{'OS-EXT-IPS-MAC:mac_addr': '02:3d:ec:f6:f8:25',
'OS-EXT-IPS:type': 'fixed',
'addr': '192.168.0.3',
'version': 4}]},
'adminPass': None,
'block_device_mapping_v2': None,
'config_drive': '',
'created': '2018-11-09T04:54:32Z',
'flavor': {'id': '42',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/42',
'rel': 'bookmark'}]},
'flavorRef': None,
'hostId': 'dd853263647ff603cfe0726dc78e34e0b9b2fc092bead501af233bcf',
'id': '2cf8db13-312a-4307-ba38-43b727ebcce6',
'image': {'id': '8254d703-e46f-4101-88ff-6f40bf7df51a',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/images/8254d703-e46f-4101-88ff-6f40bf7df51a',
'rel': 'bookmark'}]},
'imageRef': None,
'key_name': 'tellurium-key',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/servers/2cf8db13-312a-4307-ba38-43b727ebcce6',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/servers/2cf8db13-312a-4307-ba38-43b727ebcce6',
'rel': 'bookmark'}],
'metadata': {},
'name': 'tellurium_win_instance',
'networks': None,
'os-extended-volumes:volumes_attached': [],
'personality': None,
'progress': 0,
'security_groups': [{'name': 'default'}],
'status': 'ACTIVE',
'tenant_id': '9824a7403a1b411d8d207d26218597ce',
'updated': '2018-11-09T04:55:58Z',
'user_id': '7f71ea7b8abd41e5a1f303f4a1bc16b9'},
{'OS-DCF:diskConfig': 'MANUAL',
'OS-EXT-AZ:availability_zone': 'nova',
'OS-EXT-SRV-ATTR:hypervisor_hostname': None,
'OS-EXT-SRV-ATTR:instance_name': None,
'OS-EXT-SRV-ATTR:user_data': None,
'OS-EXT-STS:power_state': 1,
'OS-EXT-STS:task_state': None,
'OS-EXT-STS:vm_state': 'active',
'OS-SCH-HNT:scheduler_hints': None,
'OS-SRV-USG:launched_at': '2018-11-09T04:57:15.000000',
'OS-SRV-USG:terminated_at': None,
'accessIPv4': '',
'accessIPv6': '',
'addresses': {'tellurium_net': [{'OS-EXT-IPS-MAC:mac_addr': '02:cb:33:6b:60:f2',
'OS-EXT-IPS:type': 'fixed',
'addr': '192.168.0.5',
'version': 4}]},
'adminPass': None,
'block_device_mapping_v2': None,
'config_drive': '',
'created': '2018-11-09T04:54:32Z',
'flavor': {'id': '42',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/42',
'rel': 'bookmark'}]},
'flavorRef': None,
'hostId': 'b357f8b6b7368a07bb2aed776c6579d132c90f7dcb2f28a2a34c98e8',
'id': 'b6316031-e629-48b8-aac5-3f1b21ffe0f3',
'image': {'id': '8254d703-e46f-4101-88ff-6f40bf7df51a',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/images/8254d703-e46f-4101-88ff-6f40bf7df51a',
'rel': 'bookmark'}]},
'imageRef': None,
'key_name': 'tellurium-key-phrase',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/servers/b6316031-e629-48b8-aac5-3f1b21ffe0f3',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/servers/b6316031-e629-48b8-aac5-3f1b21ffe0f3',
'rel': 'bookmark'}],
'metadata': {},
'name': 'tellurium_win_instance_ssh_pass',
'networks': None,
'os-extended-volumes:volumes_attached': [],
'personality': None,
'progress': 0,
'security_groups': [{'name': 'default'}],
'status': 'ACTIVE',
'tenant_id': '9824a7403a1b411d8d207d26218597ce',
'updated': '2018-11-09T04:59:08Z',
'user_id': '7f71ea7b8abd41e5a1f303f4a1bc16b9'},
{'OS-DCF:diskConfig': 'MANUAL',
'OS-EXT-AZ:availability_zone': 'nova',
'OS-EXT-SRV-ATTR:hypervisor_hostname': None,
'OS-EXT-SRV-ATTR:instance_name': None,
'OS-EXT-SRV-ATTR:user_data': None,
'OS-EXT-STS:power_state': 1,
'OS-EXT-STS:task_state': None,
'OS-EXT-STS:vm_state': 'active',
'OS-SCH-HNT:scheduler_hints': None,
'OS-SRV-USG:launched_at': '2018-11-09T04:54:36.000000',
'OS-SRV-USG:terminated_at': None,
'accessIPv4': '',
'accessIPv6': '',
'addresses': {'tellurium_net': [{'OS-EXT-IPS-MAC:mac_addr': '02:20:6d:f7:16:da',
'OS-EXT-IPS:type': 'fixed',
'addr': '192.168.0.4',
'version': 4}]},
'adminPass': None,
'block_device_mapping_v2': None,
'config_drive': '',
'created': '2018-11-09T04:54:32Z',
'flavor': {'id': '16',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/flavors/16',
'rel': 'bookmark'}]},
'flavorRef': None,
'hostId': 'a5586828459ec792613bc24c0807801ce4341a562d47ff3203d41722',
'id': '4add78fa-93df-4550-aaf7-aba2239ba00a',
'image': {'id': 'dd7d4b21-79b8-42f0-8464-0d1a5274c638',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/images/dd7d4b21-79b8-42f0-8464-0d1a5274c638',
'rel': 'bookmark'}]},
'imageRef': None,
'key_name': 'tellurium-key',
'links': [{'href': 'https://compute.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/servers/4add78fa-93df-4550-aaf7-aba2239ba00a',
'rel': 'self'},
{'href': 'https://compute.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/servers/4add78fa-93df-4550-aaf7-aba2239ba00a',
'rel': 'bookmark'}],
'metadata': {},
'name': 'tellurium_instance',
'networks': None,
'os-extended-volumes:volumes_attached': [],
'personality': None,
'progress': 0,
'security_groups': [{'name': 'default'}],
'status': 'ACTIVE',
'tenant_id': '9824a7403a1b411d8d207d26218597ce',
'updated': '2018-11-09T05:22:45Z',
'user_id': '7f71ea7b8abd41e5a1f303f4a1bc16b9'}]

View File

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
NAME = 'network.subnets'
FIXTURES = [{'allocation_pools': [{'end': '192.168.0.254', 'start': '192.168.0.2'}],
'cidr': '192.168.0.0/24',
'created_at': None,
'description': None,
'dns_nameservers': ['8.8.8.8'],
'enable_dhcp': True,
'gateway_ip': '192.168.0.1',
'host_routes': [],
'id': '541f0782-587f-428b-bd79-ca227a66973b',
'ip_version': 4,
'ipv6_address_mode': None,
'ipv6_ra_mode': None,
'name': 'tellurium_net_subnet',
'network_id': 'f054013d-7052-4708-9c72-2948a329fac3',
'revision_number': None,
'segment_id': None,
'service_types': None,
'subnetpool_id': None,
'tags': [],
'tenant_id': '9824a7403a1b411d8d207d26218597ce',
'updated_at': None,
'use_default_subnetpool': None}]

View File

@ -0,0 +1,102 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
NAME = 'volume.volumes'
FIXTURES = [{'attachments': [{'attached_at': '2018-11-09T13:39:41.000000',
'attachment_id': '7edd34f8-5c1b-4c46-afac-916db0e10726',
'device': '/dev/vda',
'host_name': None,
'id': '8ca10346-fe4c-4e68-9a18-98df875d1ecc',
'server_id': '99c42e21-5099-4903-9121-063925aad299',
'volume_id': '8ca10346-fe4c-4e68-9a18-98df875d1ecc'}],
'availability_zone': 'prd1',
'bootable': True,
'consistencygroup_id': None,
'created_at': '2018-11-09T13:34:08.000000',
'description': '',
'encrypted': False,
'id': '8ca10346-fe4c-4e68-9a18-98df875d1ecc',
'imageRef': None,
'links': [{'href': 'https://volume.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/volumes/8ca10346-fe4c-4e68-9a18-98df875d1ecc',
'rel': 'self'},
{'href': 'https://volume.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/volumes/8ca10346-fe4c-4e68-9a18-98df875d1ecc',
'rel': 'bookmark'}],
'metadata': {'attached_mode': 'rw', 'readonly': 'False'},
'name': 'Ubuntu 14.04',
'os-vol-host-attr:host': None,
'os-vol-mig-status-attr:migstat': None,
'os-vol-mig-status-attr:name_id': None,
'os-vol-tenant-attr:tenant_id': '9824a7403a1b411d8d207d26218597ce',
'os-volume-replication:driver_data': None,
'os-volume-replication:extended_status': None,
'replication_status': 'disabled',
'size': 20,
'snapshot_id': None,
'source_volid': None,
'status': 'in-use',
'volume_image_metadata': {'checksum': '8ec802fe753dfe8e226645a2e0106bf7',
'container_format': 'bare',
'cw_cat': 'open_source',
'cw_logo': 'lin-ubuntu.png',
'cw_origin': 'Cloudwatt',
'cw_os': 'Ubuntu',
'disk_format': 'qcow2',
'hw_cpu_max_sockets': '1',
'hw_rng_model': 'virtio',
'image_id': '70a9c910-dd99-4065-bce9-11e89bc479fe',
'image_name': 'Ubuntu 14.04',
'min_disk': '20',
'min_ram': '0',
'size': '1009057792'},
'volume_type': 'standard'},
{'attachments': [],
'availability_zone': 'prd1',
'bootable': False,
'consistencygroup_id': None,
'created_at': '2018-11-09T04:54:29.000000',
'description': None,
'encrypted': False,
'id': '34ce951a-f2d9-4bdd-904d-9f70269c680b',
'imageRef': None,
'links': [{'href': 'https://volume.fr1.cloudwatt.com/v2/9824a7403a1b411d8d207d26218597ce/volumes/34ce951a-f2d9-4bdd-904d-9f70269c680b',
'rel': 'self'},
{'href': 'https://volume.fr1.cloudwatt.com/9824a7403a1b411d8d207d26218597ce/volumes/34ce951a-f2d9-4bdd-904d-9f70269c680b',
'rel': 'bookmark'}],
'metadata': {},
'name': 'tellurium_volume',
'os-vol-host-attr:host': None,
'os-vol-mig-status-attr:migstat': None,
'os-vol-mig-status-attr:name_id': None,
'os-vol-tenant-attr:tenant_id': '9824a7403a1b411d8d207d26218597ce',
'os-volume-replication:driver_data': None,
'os-volume-replication:extended_status': None,
'replication_status': 'disabled',
'size': 5,
'snapshot_id': None,
'source_volid': None,
'status': 'available',
'volume_image_metadata': {},
'volume_type': 'standard'}]

View File

View File

@ -0,0 +1,225 @@
description: Generated template
heat_template_version: 2013-05-23
parameters:
external_network_for_floating_ip_0:
default: 6ea98324-0f14-49f6-97c0-885d1b8dc517
description: Network to allocate floating IP from
type: string
router_0_external_network:
default: 6ea98324-0f14-49f6-97c0-885d1b8dc517
description: Router external network
type: string
server_0_flavor:
default: '17'
description: Flavor to use for server server_0
type: string
server_1_flavor:
default: '42'
description: Flavor to use for server server_1
type: string
server_1_image:
default: 8254d703-e46f-4101-88ff-6f40bf7df51a
description: Image to use for server server_1
type: string
server_2_flavor:
default: '42'
description: Flavor to use for server server_2
type: string
server_2_image:
default: 8254d703-e46f-4101-88ff-6f40bf7df51a
description: Image to use for server server_2
type: string
server_3_flavor:
default: '16'
description: Flavor to use for server server_3
type: string
server_3_image:
default: dd7d4b21-79b8-42f0-8464-0d1a5274c638
description: Image to use for server server_3
type: string
volume_0_image:
default: 70a9c910-dd99-4065-bce9-11e89bc479fe
description: Image to create volume volume_0 from
type: string
volume_0_volume_type:
default: standard
description: Volume type for volume volume_0
type: string
volume_1_volume_type:
default: standard
description: Volume type for volume volume_1
type: string
resources:
floating_ip_0:
properties:
floating_network_id:
get_param: external_network_for_floating_ip_0
type: OS::Neutron::FloatingIP
keypair_0:
properties:
name: tellurium-key
public_key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwp5kVZ+3baPZllNXZDG2mivd5nJ5wWY6Jj/WV6NlO6cUiaH5om6itU3lyJxtAbLgbbvY2FjMg1PI2JI3EHx0OSPEbDdeNsQGi31qyuiB1S5p6TreI0Dfy0tywJ9G2CURjkuJnC8SvnMfVYLMFBvx7p8RzxSdDm/zrmc4KY3ktdfYQDNtEiH2jucUUiY0ipVkDNhhv03+5C9cnpaIcVDBkddE/KEME8NIIh7s6aYCXJEWJx85nOVVRD5qK7ouV6FcGVn6zqWRD3jn0iSxcFwiKx7p6M77PmJAY4gBIpWQmutok6T4ZrXxa7jE4dybwo5e8dyvGyc7WWGqXcmcinUWr
Generated-by-Nova
type: OS::Nova::KeyPair
keypair_1:
properties:
name: tellurium-key-phrase
public_key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCaYaTC6KCC2GURFMpRQiRq8Og10PcBbVnrQqluzi2E5vAqTHAegzivmGB8h2xrjXUfbKV2bE8kz2ZE66sq1yLO/jZ+rkOA+zLnOxNwkk3gQq57b26ZegPL2tTgottJTPGyPz6v2+LLtDf/+xTJMjkPKcMWylu12Js0XKVkdY35fwN7fRvA4xghtGu1GcmS2XFDMmeLDrG1KNPbBLj5cGoD723Ho7ZhAjLThoY/xMN2OYsSNzrg3S00QngZMzQvJf0ETrB3GtITk5FUs54qMRfiyUC72lw0gLTIJK8rs7fX/yeO+467afRt2xwHN50rEWJPeTVYWdlH/msh7NQ3LZ8/
root@9b638ff21113
type: OS::Nova::KeyPair
network_1:
properties:
admin_state_up: true
name: tellurium_net
shared: false
type: OS::Neutron::Net
router_0:
properties:
admin_state_up: true
name: tellurium_router
type: OS::Neutron::Router
router_0_gateway:
properties:
network_id:
get_param: router_0_external_network
router_id:
get_resource: router_0
type: OS::Neutron::RouterGateway
router_0_interface_3:
properties:
router_id:
get_resource: router_0
subnet_id:
get_resource: subnet_0
type: OS::Neutron::RouterInterface
security_group_0:
properties:
name: _default
rules:
- direction: ingress
ethertype: IPv4
remote_mode: remote_group_id
- direction: ingress
ethertype: IPv6
remote_mode: remote_group_id
- direction: egress
ethertype: IPv4
remote_ip_prefix: 0.0.0.0/0
- direction: egress
ethertype: IPv6
remote_ip_prefix: ::/0
type: OS::Neutron::SecurityGroup
security_group_1:
properties:
description: ''
name: http
rules:
- direction: egress
ethertype: IPv4
remote_ip_prefix: 0.0.0.0/0
- direction: egress
ethertype: IPv6
- direction: ingress
ethertype: IPv4
port_range_max: 80
port_range_min: 80
protocol: tcp
remote_ip_prefix: 0.0.0.0/0
type: OS::Neutron::SecurityGroup
server_0:
properties:
block_device_mapping_v2:
- device_name: /dev/vda
volume_id:
get_resource: volume_0
flavor:
get_param: server_0_flavor
key_name:
get_resource: keypair_0
name: Ubuntu 14.04
networks:
- network:
get_resource: network_1
security_groups:
- get_resource: security_group_1
- get_resource: security_group_0
type: OS::Nova::Server
server_1:
properties:
flavor:
get_param: server_1_flavor
image:
get_param: server_1_image
key_name:
get_resource: keypair_0
name: tellurium_win_instance
networks:
- network:
get_resource: network_1
security_groups:
- get_resource: security_group_0
type: OS::Nova::Server
server_2:
properties:
flavor:
get_param: server_2_flavor
image:
get_param: server_2_image
key_name:
get_resource: keypair_1
name: tellurium_win_instance_ssh_pass
networks:
- network:
get_resource: network_1
security_groups:
- get_resource: security_group_0
type: OS::Nova::Server
server_3:
properties:
flavor:
get_param: server_3_flavor
image:
get_param: server_3_image
key_name:
get_resource: keypair_0
name: tellurium_instance
networks:
- network:
get_resource: network_1
security_groups:
- get_resource: security_group_0
type: OS::Nova::Server
subnet_0:
properties:
allocation_pools:
- end: 192.168.0.254
start: 192.168.0.2
cidr: 192.168.0.0/24
dns_nameservers:
- 8.8.8.8
enable_dhcp: true
host_routes: []
ip_version: 4
name: tellurium_net_subnet
network_id:
get_resource: network_1
type: OS::Neutron::Subnet
volume_0:
properties:
image:
get_param: volume_0_image
metadata:
attached_mode: rw
readonly: 'False'
name: Ubuntu 14.04
size: 20
volume_type:
get_param: volume_0_volume_type
type: OS::Cinder::Volume
volume_1:
properties:
name: tellurium_volume
size: 5
volume_type:
get_param: volume_1_volume_type
type: OS::Cinder::Volume

View File

@ -0,0 +1,351 @@
action: CREATE
environment:
parameter_defaults: {}
parameters: {}
resources:
floating_ip_0:
action: CREATE
metadata: {}
name: floating_ip_0
resource_data: {}
resource_id: e6f50641-6c7e-468c-9623-1fd5ee2c1ebc
status: COMPLETE
type: OS::Neutron::FloatingIP
keypair_0:
action: CREATE
metadata: {}
name: keypair_0
resource_data: {}
resource_id: tellurium-key
status: COMPLETE
type: OS::Nova::KeyPair
keypair_1:
action: CREATE
metadata: {}
name: keypair_1
resource_data: {}
resource_id: tellurium-key-phrase
status: COMPLETE
type: OS::Nova::KeyPair
network_1:
action: CREATE
metadata: {}
name: network_1
resource_data: {}
resource_id: f054013d-7052-4708-9c72-2948a329fac3
status: COMPLETE
type: OS::Neutron::Net
router_0:
action: CREATE
metadata: {}
name: router_0
resource_data: {}
resource_id: ebc6dd0c-a276-4368-8084-bd37c587cc24
status: COMPLETE
type: OS::Neutron::Router
router_0_gateway:
action: CREATE
metadata: {}
name: router_0_gateway
resource_data: {}
resource_id: ebc6dd0c-a276-4368-8084-bd37c587cc24:6ea98324-0f14-49f6-97c0-885d1b8dc517
status: COMPLETE
type: OS::Neutron::RouterGateway
router_0_interface_3:
action: CREATE
metadata: {}
name: router_0_interface_3
resource_data: {}
resource_id: ebc6dd0c-a276-4368-8084-bd37c587cc24:subnet_id=541f0782-587f-428b-bd79-ca227a66973b
status: COMPLETE
type: OS::Neutron::RouterInterface
security_group_1:
action: CREATE
metadata: {}
name: security_group_1
resource_data: {}
resource_id: 156799a3-565e-48b3-938c-f95f09093c66
status: COMPLETE
type: OS::Neutron::SecurityGroup
server_0:
action: CREATE
metadata: {}
name: server_0
resource_data: {}
resource_id: 99c42e21-5099-4903-9121-063925aad299
status: COMPLETE
type: OS::Nova::Server
server_1:
action: CREATE
metadata: {}
name: server_1
resource_data: {}
resource_id: 2cf8db13-312a-4307-ba38-43b727ebcce6
status: COMPLETE
type: OS::Nova::Server
server_2:
action: CREATE
metadata: {}
name: server_2
resource_data: {}
resource_id: b6316031-e629-48b8-aac5-3f1b21ffe0f3
status: COMPLETE
type: OS::Nova::Server
server_3:
action: CREATE
metadata: {}
name: server_3
resource_data: {}
resource_id: 4add78fa-93df-4550-aaf7-aba2239ba00a
status: COMPLETE
type: OS::Nova::Server
subnet_0:
action: CREATE
metadata: {}
name: subnet_0
resource_data: {}
resource_id: 541f0782-587f-428b-bd79-ca227a66973b
status: COMPLETE
type: OS::Neutron::Subnet
volume_0:
action: CREATE
metadata: {}
name: volume_0
resource_data: {}
resource_id: 8ca10346-fe4c-4e68-9a18-98df875d1ecc
status: COMPLETE
type: OS::Cinder::Volume
volume_1:
action: CREATE
metadata: {}
name: volume_1
resource_data: {}
resource_id: 34ce951a-f2d9-4bdd-904d-9f70269c680b
status: COMPLETE
type: OS::Cinder::Volume
status: COMPLETE
template:
description: Generated template
heat_template_version: 2013-05-23
parameters:
external_network_for_floating_ip_0:
default: 6ea98324-0f14-49f6-97c0-885d1b8dc517
description: Network to allocate floating IP from
type: string
router_0_external_network:
default: 6ea98324-0f14-49f6-97c0-885d1b8dc517
description: Router external network
type: string
server_0_default_security_group:
default: 9ffd2654-7ca6-48ae-852d-6503d5ce4a60
description: Default security group for server Ubuntu 14.04
type: string
server_0_flavor:
default: '17'
description: Flavor to use for server server_0
type: string
server_1_default_security_group:
default: 9ffd2654-7ca6-48ae-852d-6503d5ce4a60
description: Default security group for server tellurium_win_instance
type: string
server_1_flavor:
default: '42'
description: Flavor to use for server server_1
type: string
server_1_image:
default: 8254d703-e46f-4101-88ff-6f40bf7df51a
description: Image to use for server server_1
type: string
server_2_default_security_group:
default: 9ffd2654-7ca6-48ae-852d-6503d5ce4a60
description: Default security group for server tellurium_win_instance_ssh_pass
type: string
server_2_flavor:
default: '42'
description: Flavor to use for server server_2
type: string
server_2_image:
default: 8254d703-e46f-4101-88ff-6f40bf7df51a
description: Image to use for server server_2
type: string
server_3_default_security_group:
default: 9ffd2654-7ca6-48ae-852d-6503d5ce4a60
description: Default security group for server tellurium_instance
type: string
server_3_flavor:
default: '16'
description: Flavor to use for server server_3
type: string
server_3_image:
default: dd7d4b21-79b8-42f0-8464-0d1a5274c638
description: Image to use for server server_3
type: string
volume_0_image:
default: 70a9c910-dd99-4065-bce9-11e89bc479fe
description: Image to create volume volume_0 from
type: string
volume_0_volume_type:
default: standard
description: Volume type for volume volume_0
type: string
volume_1_volume_type:
default: standard
description: Volume type for volume volume_1
type: string
resources:
floating_ip_0:
properties:
floating_network_id:
get_param: external_network_for_floating_ip_0
type: OS::Neutron::FloatingIP
keypair_0:
properties:
name: tellurium-key
public_key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwp5kVZ+3baPZllNXZDG2mivd5nJ5wWY6Jj/WV6NlO6cUiaH5om6itU3lyJxtAbLgbbvY2FjMg1PI2JI3EHx0OSPEbDdeNsQGi31qyuiB1S5p6TreI0Dfy0tywJ9G2CURjkuJnC8SvnMfVYLMFBvx7p8RzxSdDm/zrmc4KY3ktdfYQDNtEiH2jucUUiY0ipVkDNhhv03+5C9cnpaIcVDBkddE/KEME8NIIh7s6aYCXJEWJx85nOVVRD5qK7ouV6FcGVn6zqWRD3jn0iSxcFwiKx7p6M77PmJAY4gBIpWQmutok6T4ZrXxa7jE4dybwo5e8dyvGyc7WWGqXcmcinUWr
Generated-by-Nova
type: OS::Nova::KeyPair
keypair_1:
properties:
name: tellurium-key-phrase
public_key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCaYaTC6KCC2GURFMpRQiRq8Og10PcBbVnrQqluzi2E5vAqTHAegzivmGB8h2xrjXUfbKV2bE8kz2ZE66sq1yLO/jZ+rkOA+zLnOxNwkk3gQq57b26ZegPL2tTgottJTPGyPz6v2+LLtDf/+xTJMjkPKcMWylu12Js0XKVkdY35fwN7fRvA4xghtGu1GcmS2XFDMmeLDrG1KNPbBLj5cGoD723Ho7ZhAjLThoY/xMN2OYsSNzrg3S00QngZMzQvJf0ETrB3GtITk5FUs54qMRfiyUC72lw0gLTIJK8rs7fX/yeO+467afRt2xwHN50rEWJPeTVYWdlH/msh7NQ3LZ8/
root@9b638ff21113
type: OS::Nova::KeyPair
network_1:
properties:
admin_state_up: true
name: tellurium_net
shared: false
type: OS::Neutron::Net
router_0:
properties:
admin_state_up: true
name: tellurium_router
type: OS::Neutron::Router
router_0_gateway:
properties:
network_id:
get_param: router_0_external_network
router_id:
get_resource: router_0
type: OS::Neutron::RouterGateway
router_0_interface_3:
properties:
router_id:
get_resource: router_0
subnet_id:
get_resource: subnet_0
type: OS::Neutron::RouterInterface
security_group_1:
properties:
description: ''
name: http
rules:
- direction: egress
ethertype: IPv4
remote_ip_prefix: 0.0.0.0/0
- direction: egress
ethertype: IPv6
- direction: ingress
ethertype: IPv4
port_range_max: 80
port_range_min: 80
protocol: tcp
remote_ip_prefix: 0.0.0.0/0
type: OS::Neutron::SecurityGroup
server_0:
properties:
block_device_mapping_v2:
- device_name: /dev/vda
volume_id:
get_resource: volume_0
flavor:
get_param: server_0_flavor
key_name:
get_resource: keypair_0
name: Ubuntu 14.04
networks:
- network:
get_resource: network_1
security_groups:
- get_resource: security_group_1
- get_param: server_0_default_security_group
type: OS::Nova::Server
server_1:
properties:
flavor:
get_param: server_1_flavor
image:
get_param: server_1_image
key_name:
get_resource: keypair_0
name: tellurium_win_instance
networks:
- network:
get_resource: network_1
security_groups:
- get_param: server_1_default_security_group
type: OS::Nova::Server
server_2:
properties:
flavor:
get_param: server_2_flavor
image:
get_param: server_2_image
key_name:
get_resource: keypair_1
name: tellurium_win_instance_ssh_pass
networks:
- network:
get_resource: network_1
security_groups:
- get_param: server_2_default_security_group
type: OS::Nova::Server
server_3:
properties:
flavor:
get_param: server_3_flavor
image:
get_param: server_3_image
key_name:
get_resource: keypair_0
name: tellurium_instance
networks:
- network:
get_resource: network_1
security_groups:
- get_param: server_3_default_security_group
type: OS::Nova::Server
subnet_0:
properties:
allocation_pools:
- end: 192.168.0.254
start: 192.168.0.2
cidr: 192.168.0.0/24
dns_nameservers:
- 8.8.8.8
enable_dhcp: true
host_routes: []
ip_version: 4
name: tellurium_net_subnet
network_id:
get_resource: network_1
type: OS::Neutron::Subnet
volume_0:
properties:
image:
get_param: volume_0_image
metadata:
attached_mode: rw
readonly: 'False'
name: Ubuntu 14.04
size: 20
volume_type:
get_param: volume_0_volume_type
type: OS::Cinder::Volume
volume_1:
properties:
name: tellurium_volume
size: 5
volume_type:
get_param: volume_1_volume_type
type: OS::Cinder::Volume

View File

@ -0,0 +1,237 @@
description: Generated template
heat_template_version: 2013-05-23
parameters:
external_network_for_floating_ip_0:
constraints:
- custom_constraint: neutron.network
default: 6ea98324-0f14-49f6-97c0-885d1b8dc517
description: Network to allocate floating IP from
type: string
router_0_external_network:
constraints:
- custom_constraint: neutron.network
default: 6ea98324-0f14-49f6-97c0-885d1b8dc517
description: Router external network
type: string
server_0_flavor:
default: '17'
description: Flavor to use for server server_0
type: string
server_1_flavor:
default: '42'
description: Flavor to use for server server_1
type: string
server_1_image:
constraints:
- custom_constraint: glance.image
default: 8254d703-e46f-4101-88ff-6f40bf7df51a
description: Image to use for server server_1
type: string
server_2_flavor:
default: '42'
description: Flavor to use for server server_2
type: string
server_2_image:
constraints:
- custom_constraint: glance.image
default: 8254d703-e46f-4101-88ff-6f40bf7df51a
description: Image to use for server server_2
type: string
server_3_flavor:
default: '16'
description: Flavor to use for server server_3
type: string
server_3_image:
constraints:
- custom_constraint: glance.image
default: dd7d4b21-79b8-42f0-8464-0d1a5274c638
description: Image to use for server server_3
type: string
volume_0_image:
constraints:
- custom_constraint: glance.image
default: 70a9c910-dd99-4065-bce9-11e89bc479fe
description: Image to create volume volume_0 from
type: string
volume_0_volume_type:
default: standard
description: Volume type for volume volume_0
type: string
volume_1_volume_type:
default: standard
description: Volume type for volume volume_1
type: string
resources:
floating_ip_0:
properties:
floating_network_id:
get_param: external_network_for_floating_ip_0
type: OS::Neutron::FloatingIP
keypair_0:
properties:
name: tellurium-key
public_key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwp5kVZ+3baPZllNXZDG2mivd5nJ5wWY6Jj/WV6NlO6cUiaH5om6itU3lyJxtAbLgbbvY2FjMg1PI2JI3EHx0OSPEbDdeNsQGi31qyuiB1S5p6TreI0Dfy0tywJ9G2CURjkuJnC8SvnMfVYLMFBvx7p8RzxSdDm/zrmc4KY3ktdfYQDNtEiH2jucUUiY0ipVkDNhhv03+5C9cnpaIcVDBkddE/KEME8NIIh7s6aYCXJEWJx85nOVVRD5qK7ouV6FcGVn6zqWRD3jn0iSxcFwiKx7p6M77PmJAY4gBIpWQmutok6T4ZrXxa7jE4dybwo5e8dyvGyc7WWGqXcmcinUWr
Generated-by-Nova
type: OS::Nova::KeyPair
keypair_1:
properties:
name: tellurium-key-phrase
public_key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCaYaTC6KCC2GURFMpRQiRq8Og10PcBbVnrQqluzi2E5vAqTHAegzivmGB8h2xrjXUfbKV2bE8kz2ZE66sq1yLO/jZ+rkOA+zLnOxNwkk3gQq57b26ZegPL2tTgottJTPGyPz6v2+LLtDf/+xTJMjkPKcMWylu12Js0XKVkdY35fwN7fRvA4xghtGu1GcmS2XFDMmeLDrG1KNPbBLj5cGoD723Ho7ZhAjLThoY/xMN2OYsSNzrg3S00QngZMzQvJf0ETrB3GtITk5FUs54qMRfiyUC72lw0gLTIJK8rs7fX/yeO+467afRt2xwHN50rEWJPeTVYWdlH/msh7NQ3LZ8/
root@9b638ff21113
type: OS::Nova::KeyPair
network_1:
properties:
admin_state_up: true
name: tellurium_net
shared: false
type: OS::Neutron::Net
router_0:
properties:
admin_state_up: true
name: tellurium_router
type: OS::Neutron::Router
router_0_gateway:
properties:
network_id:
get_param: router_0_external_network
router_id:
get_resource: router_0
type: OS::Neutron::RouterGateway
router_0_interface_3:
properties:
router_id:
get_resource: router_0
subnet_id:
get_resource: subnet_0
type: OS::Neutron::RouterInterface
security_group_0:
properties:
name: _default
rules:
- direction: ingress
ethertype: IPv4
remote_mode: remote_group_id
- direction: ingress
ethertype: IPv6
remote_mode: remote_group_id
- direction: egress
ethertype: IPv4
remote_ip_prefix: 0.0.0.0/0
- direction: egress
ethertype: IPv6
remote_ip_prefix: ::/0
type: OS::Neutron::SecurityGroup
security_group_1:
properties:
description: ''
name: http
rules:
- direction: egress
ethertype: IPv4
remote_ip_prefix: 0.0.0.0/0
- direction: egress
ethertype: IPv6
- direction: ingress
ethertype: IPv4
port_range_max: 80
port_range_min: 80
protocol: tcp
remote_ip_prefix: 0.0.0.0/0
type: OS::Neutron::SecurityGroup
server_0:
properties:
block_device_mapping_v2:
- device_name: /dev/vda
volume_id:
get_resource: volume_0
flavor:
get_param: server_0_flavor
key_name:
get_resource: keypair_0
name: Ubuntu 14.04
networks:
- network:
get_resource: network_1
security_groups:
- get_resource: security_group_1
- get_resource: security_group_0
type: OS::Nova::Server
server_1:
properties:
flavor:
get_param: server_1_flavor
image:
get_param: server_1_image
key_name:
get_resource: keypair_0
name: tellurium_win_instance
networks:
- network:
get_resource: network_1
security_groups:
- get_resource: security_group_0
type: OS::Nova::Server
server_2:
properties:
flavor:
get_param: server_2_flavor
image:
get_param: server_2_image
key_name:
get_resource: keypair_1
name: tellurium_win_instance_ssh_pass
networks:
- network:
get_resource: network_1
security_groups:
- get_resource: security_group_0
type: OS::Nova::Server
server_3:
properties:
flavor:
get_param: server_3_flavor
image:
get_param: server_3_image
key_name:
get_resource: keypair_0
name: tellurium_instance
networks:
- network:
get_resource: network_1
security_groups:
- get_resource: security_group_0
type: OS::Nova::Server
subnet_0:
properties:
allocation_pools:
- end: 192.168.0.254
start: 192.168.0.2
cidr: 192.168.0.0/24
dns_nameservers:
- 8.8.8.8
enable_dhcp: true
host_routes: []
ip_version: 4
name: tellurium_net_subnet
network_id:
get_resource: network_1
type: OS::Neutron::Subnet
volume_0:
properties:
image:
get_param: volume_0_image
metadata:
attached_mode: rw
readonly: 'False'
name: Ubuntu 14.04
size: 20
volume_type:
get_param: volume_0_volume_type
type: OS::Cinder::Volume
volume_1:
properties:
name: tellurium_volume
size: 5
volume_type:
get_param: volume_1_volume_type
type: OS::Cinder::Volume

View File

@ -0,0 +1,289 @@
description: Generated template
heat_template_version: 2013-05-23
parameters:
external_network_for_floating_ip_0:
constraints:
- custom_constraint: neutron.network
default: 6ea98324-0f14-49f6-97c0-885d1b8dc517
description: Network to allocate floating IP from
type: string
router_0_external_network:
constraints:
- custom_constraint: neutron.network
default: 6ea98324-0f14-49f6-97c0-885d1b8dc517
description: Router external network
type: string
server_0_flavor:
default: '17'
description: Flavor to use for server server_0
type: string
server_1_flavor:
default: '42'
description: Flavor to use for server server_1
type: string
server_1_image:
constraints:
- custom_constraint: glance.image
default: 8254d703-e46f-4101-88ff-6f40bf7df51a
description: Image to use for server server_1
type: string
server_2_flavor:
default: '42'
description: Flavor to use for server server_2
type: string
server_2_image:
constraints:
- custom_constraint: glance.image
default: 8254d703-e46f-4101-88ff-6f40bf7df51a
description: Image to use for server server_2
type: string
server_3_flavor:
default: '16'
description: Flavor to use for server server_3
type: string
server_3_image:
constraints:
- custom_constraint: glance.image
default: dd7d4b21-79b8-42f0-8464-0d1a5274c638
description: Image to use for server server_3
type: string
volume_0_image:
constraints:
- custom_constraint: glance.image
default: 70a9c910-dd99-4065-bce9-11e89bc479fe
description: Image to create volume volume_0 from
type: string
volume_0_volume_type:
default: standard
description: Volume type for volume volume_0
type: string
volume_1_volume_type:
default: standard
description: Volume type for volume volume_1
type: string
resources:
floating_ip_0:
properties:
floating_network_id:
get_param: external_network_for_floating_ip_0
type: OS::Neutron::FloatingIP
keypair_0:
properties:
name: tellurium-key
public_key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwp5kVZ+3baPZllNXZDG2mivd5nJ5wWY6Jj/WV6NlO6cUiaH5om6itU3lyJxtAbLgbbvY2FjMg1PI2JI3EHx0OSPEbDdeNsQGi31qyuiB1S5p6TreI0Dfy0tywJ9G2CURjkuJnC8SvnMfVYLMFBvx7p8RzxSdDm/zrmc4KY3ktdfYQDNtEiH2jucUUiY0ipVkDNhhv03+5C9cnpaIcVDBkddE/KEME8NIIh7s6aYCXJEWJx85nOVVRD5qK7ouV6FcGVn6zqWRD3jn0iSxcFwiKx7p6M77PmJAY4gBIpWQmutok6T4ZrXxa7jE4dybwo5e8dyvGyc7WWGqXcmcinUWr
Generated-by-Nova
type: OS::Nova::KeyPair
keypair_1:
properties:
name: tellurium-key-phrase
public_key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCaYaTC6KCC2GURFMpRQiRq8Og10PcBbVnrQqluzi2E5vAqTHAegzivmGB8h2xrjXUfbKV2bE8kz2ZE66sq1yLO/jZ+rkOA+zLnOxNwkk3gQq57b26ZegPL2tTgottJTPGyPz6v2+LLtDf/+xTJMjkPKcMWylu12Js0XKVkdY35fwN7fRvA4xghtGu1GcmS2XFDMmeLDrG1KNPbBLj5cGoD723Ho7ZhAjLThoY/xMN2OYsSNzrg3S00QngZMzQvJf0ETrB3GtITk5FUs54qMRfiyUC72lw0gLTIJK8rs7fX/yeO+467afRt2xwHN50rEWJPeTVYWdlH/msh7NQ3LZ8/
root@9b638ff21113
type: OS::Nova::KeyPair
network_1:
properties:
admin_state_up: true
name: tellurium_net
shared: false
type: OS::Neutron::Net
port_0:
properties:
admin_state_up: true
device_owner: compute:None
fixed_ips:
- ip_address: 192.168.0.3
subnet_id:
get_resource: subnet_0
mac_address: 02:3d:ec:f6:f8:25
name: Tellurium_Fixtures-windows_port-hxvnswbzi56x
network_id:
get_resource: network_1
security_groups:
- get_resource: security_group_0
type: OS::Neutron::Port
port_1:
properties:
admin_state_up: true
device_owner: compute:None
fixed_ips:
- ip_address: 192.168.0.4
subnet_id:
get_resource: subnet_0
mac_address: 02:20:6d:f7:16:da
name: Tellurium_Fixtures-instance_port-fmln6fjl4yvo
network_id:
get_resource: network_1
security_groups:
- get_resource: security_group_0
type: OS::Neutron::Port
port_2:
properties:
admin_state_up: true
device_owner: compute:None
fixed_ips:
- ip_address: 192.168.0.5
subnet_id:
get_resource: subnet_0
mac_address: 02:cb:33:6b:60:f2
name: Tellurium_Fixtures-windows_ssh_pass_port-4zlwanbetsg2
network_id:
get_resource: network_1
security_groups:
- get_resource: security_group_0
type: OS::Neutron::Port
port_4:
properties:
admin_state_up: true
device_owner: compute:nova
fixed_ips:
- ip_address: 192.168.0.6
subnet_id:
get_resource: subnet_0
mac_address: 02:4f:41:34:32:b4
name: 4f413432-b499-4fcc-a81a-1ee0d8bc16b7
network_id:
get_resource: network_1
security_groups:
- get_resource: security_group_1
- get_resource: security_group_0
type: OS::Neutron::Port
router_0:
properties:
admin_state_up: true
name: tellurium_router
type: OS::Neutron::Router
router_0_gateway:
properties:
network_id:
get_param: router_0_external_network
router_id:
get_resource: router_0
type: OS::Neutron::RouterGateway
router_0_interface_3:
properties:
router_id:
get_resource: router_0
subnet_id:
get_resource: subnet_0
type: OS::Neutron::RouterInterface
security_group_0:
properties:
name: _default
rules:
- direction: ingress
ethertype: IPv4
remote_mode: remote_group_id
- direction: ingress
ethertype: IPv6
remote_mode: remote_group_id
- direction: egress
ethertype: IPv4
remote_ip_prefix: 0.0.0.0/0
- direction: egress
ethertype: IPv6
remote_ip_prefix: ::/0
type: OS::Neutron::SecurityGroup
security_group_1:
properties:
description: ''
name: http
rules:
- direction: egress
ethertype: IPv4
remote_ip_prefix: 0.0.0.0/0
- direction: egress
ethertype: IPv6
- direction: ingress
ethertype: IPv4
port_range_max: 80
port_range_min: 80
protocol: tcp
remote_ip_prefix: 0.0.0.0/0
type: OS::Neutron::SecurityGroup
server_0:
properties:
block_device_mapping_v2:
- device_name: /dev/vda
volume_id:
get_resource: volume_0
flavor:
get_param: server_0_flavor
key_name:
get_resource: keypair_0
name: Ubuntu 14.04
networks:
- port:
get_resource: port_4
type: OS::Nova::Server
server_1:
properties:
flavor:
get_param: server_1_flavor
image:
get_param: server_1_image
key_name:
get_resource: keypair_0
name: tellurium_win_instance
networks:
- port:
get_resource: port_0
type: OS::Nova::Server
server_2:
properties:
flavor:
get_param: server_2_flavor
image:
get_param: server_2_image
key_name:
get_resource: keypair_1
name: tellurium_win_instance_ssh_pass
networks:
- port:
get_resource: port_2
type: OS::Nova::Server
server_3:
properties:
flavor:
get_param: server_3_flavor
image:
get_param: server_3_image
key_name:
get_resource: keypair_0
name: tellurium_instance
networks:
- port:
get_resource: port_1
type: OS::Nova::Server
subnet_0:
properties:
allocation_pools:
- end: 192.168.0.254
start: 192.168.0.2
cidr: 192.168.0.0/24
dns_nameservers:
- 8.8.8.8
enable_dhcp: true
host_routes: []
ip_version: 4
name: tellurium_net_subnet
network_id:
get_resource: network_1
type: OS::Neutron::Subnet
volume_0:
properties:
image:
get_param: volume_0_image
metadata:
attached_mode: rw
readonly: 'False'
name: Ubuntu 14.04
size: 20
volume_type:
get_param: volume_0_volume_type
type: OS::Cinder::Volume
volume_1:
properties:
name: tellurium_volume
size: 5
volume_type:
get_param: volume_1_volume_type
type: OS::Cinder::Volume

View File

@ -0,0 +1,277 @@
description: Generated template
heat_template_version: 2013-05-23
parameters:
external_network_for_floating_ip_0:
default: 6ea98324-0f14-49f6-97c0-885d1b8dc517
description: Network to allocate floating IP from
type: string
router_0_external_network:
default: 6ea98324-0f14-49f6-97c0-885d1b8dc517
description: Router external network
type: string
server_0_flavor:
default: '17'
description: Flavor to use for server server_0
type: string
server_1_flavor:
default: '42'
description: Flavor to use for server server_1
type: string
server_1_image:
default: 8254d703-e46f-4101-88ff-6f40bf7df51a
description: Image to use for server server_1
type: string
server_2_flavor:
default: '42'
description: Flavor to use for server server_2
type: string
server_2_image:
default: 8254d703-e46f-4101-88ff-6f40bf7df51a
description: Image to use for server server_2
type: string
server_3_flavor:
default: '16'
description: Flavor to use for server server_3
type: string
server_3_image:
default: dd7d4b21-79b8-42f0-8464-0d1a5274c638
description: Image to use for server server_3
type: string
volume_0_image:
default: 70a9c910-dd99-4065-bce9-11e89bc479fe
description: Image to create volume volume_0 from
type: string
volume_0_volume_type:
default: standard
description: Volume type for volume volume_0
type: string
volume_1_volume_type:
default: standard
description: Volume type for volume volume_1
type: string
resources:
floating_ip_0:
properties:
floating_network_id:
get_param: external_network_for_floating_ip_0
type: OS::Neutron::FloatingIP
keypair_0:
properties:
name: tellurium-key
public_key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwp5kVZ+3baPZllNXZDG2mivd5nJ5wWY6Jj/WV6NlO6cUiaH5om6itU3lyJxtAbLgbbvY2FjMg1PI2JI3EHx0OSPEbDdeNsQGi31qyuiB1S5p6TreI0Dfy0tywJ9G2CURjkuJnC8SvnMfVYLMFBvx7p8RzxSdDm/zrmc4KY3ktdfYQDNtEiH2jucUUiY0ipVkDNhhv03+5C9cnpaIcVDBkddE/KEME8NIIh7s6aYCXJEWJx85nOVVRD5qK7ouV6FcGVn6zqWRD3jn0iSxcFwiKx7p6M77PmJAY4gBIpWQmutok6T4ZrXxa7jE4dybwo5e8dyvGyc7WWGqXcmcinUWr
Generated-by-Nova
type: OS::Nova::KeyPair
keypair_1:
properties:
name: tellurium-key-phrase
public_key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCaYaTC6KCC2GURFMpRQiRq8Og10PcBbVnrQqluzi2E5vAqTHAegzivmGB8h2xrjXUfbKV2bE8kz2ZE66sq1yLO/jZ+rkOA+zLnOxNwkk3gQq57b26ZegPL2tTgottJTPGyPz6v2+LLtDf/+xTJMjkPKcMWylu12Js0XKVkdY35fwN7fRvA4xghtGu1GcmS2XFDMmeLDrG1KNPbBLj5cGoD723Ho7ZhAjLThoY/xMN2OYsSNzrg3S00QngZMzQvJf0ETrB3GtITk5FUs54qMRfiyUC72lw0gLTIJK8rs7fX/yeO+467afRt2xwHN50rEWJPeTVYWdlH/msh7NQ3LZ8/
root@9b638ff21113
type: OS::Nova::KeyPair
network_1:
properties:
admin_state_up: true
name: tellurium_net
shared: false
type: OS::Neutron::Net
port_0:
properties:
admin_state_up: true
device_owner: compute:None
fixed_ips:
- ip_address: 192.168.0.3
subnet_id:
get_resource: subnet_0
mac_address: 02:3d:ec:f6:f8:25
name: Tellurium_Fixtures-windows_port-hxvnswbzi56x
network_id:
get_resource: network_1
security_groups:
- get_resource: security_group_0
type: OS::Neutron::Port
port_1:
properties:
admin_state_up: true
device_owner: compute:None
fixed_ips:
- ip_address: 192.168.0.4
subnet_id:
get_resource: subnet_0
mac_address: 02:20:6d:f7:16:da
name: Tellurium_Fixtures-instance_port-fmln6fjl4yvo
network_id:
get_resource: network_1
security_groups:
- get_resource: security_group_0
type: OS::Neutron::Port
port_2:
properties:
admin_state_up: true
device_owner: compute:None
fixed_ips:
- ip_address: 192.168.0.5
subnet_id:
get_resource: subnet_0
mac_address: 02:cb:33:6b:60:f2
name: Tellurium_Fixtures-windows_ssh_pass_port-4zlwanbetsg2
network_id:
get_resource: network_1
security_groups:
- get_resource: security_group_0
type: OS::Neutron::Port
port_4:
properties:
admin_state_up: true
device_owner: compute:nova
fixed_ips:
- ip_address: 192.168.0.6
subnet_id:
get_resource: subnet_0
mac_address: 02:4f:41:34:32:b4
name: 4f413432-b499-4fcc-a81a-1ee0d8bc16b7
network_id:
get_resource: network_1
security_groups:
- get_resource: security_group_1
- get_resource: security_group_0
type: OS::Neutron::Port
router_0:
properties:
admin_state_up: true
name: tellurium_router
type: OS::Neutron::Router
router_0_gateway:
properties:
network_id:
get_param: router_0_external_network
router_id:
get_resource: router_0
type: OS::Neutron::RouterGateway
router_0_interface_3:
properties:
router_id:
get_resource: router_0
subnet_id:
get_resource: subnet_0
type: OS::Neutron::RouterInterface
security_group_0:
properties:
name: _default
rules:
- direction: ingress
ethertype: IPv4
remote_mode: remote_group_id
- direction: ingress
ethertype: IPv6
remote_mode: remote_group_id
- direction: egress
ethertype: IPv4
remote_ip_prefix: 0.0.0.0/0
- direction: egress
ethertype: IPv6
remote_ip_prefix: ::/0
type: OS::Neutron::SecurityGroup
security_group_1:
properties:
description: ''
name: http
rules:
- direction: egress
ethertype: IPv4
remote_ip_prefix: 0.0.0.0/0
- direction: egress
ethertype: IPv6
- direction: ingress
ethertype: IPv4
port_range_max: 80
port_range_min: 80
protocol: tcp
remote_ip_prefix: 0.0.0.0/0
type: OS::Neutron::SecurityGroup
server_0:
properties:
block_device_mapping_v2:
- device_name: /dev/vda
volume_id:
get_resource: volume_0
flavor:
get_param: server_0_flavor
key_name:
get_resource: keypair_0
name: Ubuntu 14.04
networks:
- port:
get_resource: port_4
type: OS::Nova::Server
server_1:
properties:
flavor:
get_param: server_1_flavor
image:
get_param: server_1_image
key_name:
get_resource: keypair_0
name: tellurium_win_instance
networks:
- port:
get_resource: port_0
type: OS::Nova::Server
server_2:
properties:
flavor:
get_param: server_2_flavor
image:
get_param: server_2_image
key_name:
get_resource: keypair_1
name: tellurium_win_instance_ssh_pass
networks:
- port:
get_resource: port_2
type: OS::Nova::Server
server_3:
properties:
flavor:
get_param: server_3_flavor
image:
get_param: server_3_image
key_name:
get_resource: keypair_0
name: tellurium_instance
networks:
- port:
get_resource: port_1
type: OS::Nova::Server
subnet_0:
properties:
allocation_pools:
- end: 192.168.0.254
start: 192.168.0.2
cidr: 192.168.0.0/24
dns_nameservers:
- 8.8.8.8
enable_dhcp: true
host_routes: []
ip_version: 4
name: tellurium_net_subnet
network_id:
get_resource: network_1
type: OS::Neutron::Subnet
volume_0:
properties:
image:
get_param: volume_0_image
metadata:
attached_mode: rw
readonly: 'False'
name: Ubuntu 14.04
size: 20
volume_type:
get_param: volume_0_volume_type
type: OS::Cinder::Volume
volume_1:
properties:
name: tellurium_volume
size: 5
volume_type:
get_param: volume_1_volume_type
type: OS::Cinder::Volume

124
flameclient/tests/fixtures/utils.py vendored Normal file
View File

@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
from pprint import pformat
import six
from flameclient.utils import get_deep_attr
from flameclient.utils import munchify
def get_licence():
import flameclient
licence_filename = os.path.join(
os.path.realpath(os.path.dirname(flameclient.__file__)),
'LICENSE.txt'
)
with open(licence_filename) as fp:
return fp.read()
def get_python_header():
top = '# -*- coding: utf-8 -*-'
lines = get_licence().strip().split('\n')
commented_lines = ['# %s' % line.strip() for line in lines]
commented_lines = [line.strip() for line in commented_lines]
commented_licence = '\n'.join(commented_lines)
return '%s\n\n%s\n' % (top, commented_licence)
def write_openstackcloud_fixture_data(filename, attr_name, data):
"""Write a fixtures file with given data
:param str filename: filename holding the fixtures.
:param str attr_name: attribute name of the `connection` parameter.
:param list data: a python list of data which needs to be returned
"""
fixtures = [vars(munchify(element)) for element in data]
openstackcloud_dir = os.path.join(
os.path.realpath(os.path.dirname(__file__)),
'openstackcloud'
)
if not filename.startswith('/'):
filename = os.path.join(openstackcloud_dir, filename)
head = get_python_header()
file_str = "%s\n\nNAME = '%s'\n\nFIXTURES = %s\n" % (
head, attr_name, pformat(fixtures, indent=1)
)
with open(filename, 'w') as fp:
fp.write(file_str)
def write_openstackcloud_fixture(filename, attr_name, connection):
"""Write a fixtures API call file
:param str filename: filename holding the fixtures.
:param str attr_name: attribute name of the `connection` parameter.
:param openstack.connection.Connection connection:
An `openstack.connection.Connection` instance (`openstacksdk`).
Since `shade.openstackcloud.OpenStackCloud` (`shade`) is a subclass
you can also use shade instances.
ex. to write connection.network.security_groups to a fixtures file:
write_openstackcloud_fixture(
'flameclient/tests/fixtures/openstackcloud/security_groups.py',
'network.security_groups',
connection
)
"""
method = get_deep_attr(connection, attr_name)
write_openstackcloud_fixture_data(filename, attr_name, method())
def rewrite_all_openstackcloud_fixtures(connection):
"""Rewrite all fixtures in flameclient.tests.fixtures
This function rewrites all fixture files present in
flameclient.tests.fixtures from an existing openstack session.
:param openstack.connection.Connection connection:
An `openstack.connection.Connection` instance (`openstacksdk`).
Since `shade.openstackcloud.OpenStackCloud` (`shade`) is a subclass
you can also use shade instances.
DANGEROUS! You can break everything!!!
"""
from flameclient.tests.fixtures import openstackcloud
for fixture_module in six.itervalues(openstackcloud.FIXTURES):
write_openstackcloud_fixture(
fixture_module.__file__,
fixture_module.NAME,
connection
)

File diff suppressed because it is too large Load Diff

View File

@ -1,640 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2014 Cloudwatt
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import re
import mock
from flameclient import flame
from flameclient.tests import base
class FakeBase(object):
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
class FakeVolume(FakeBase):
id = 1234
size = 1
source_volid = None
bootable = 'false'
snapshot_id = None
display_name = 'vol1'
display_description = 'Description'
volume_type = 'fast'
metadata = None
class FakeServer(FakeBase):
id = '1234'
name = 'server1'
config_drive = None
flavor = {'id': '2'}
image = {'id': '3333',
'links': [{'href': 'http://p/7777/images/3333',
'rel': 'bookmark'}]}
key_name = 'testkey'
addresses = []
metadata = None
def __init__(self, server_id, **kwargs):
self.id = server_id
kwargs.setdefault('OS-DCF:diskConfig', 'MANUAL')
kwargs.setdefault('os-extended-volumes:volumes_attached', [])
super(FakeServer, self).__init__(**kwargs)
class FakeFlavor(FakeBase):
name = 'm1.tiny'
id = '1'
class FakeKeypair(FakeBase):
name = 'key'
id = 'key'
public_key = 'ssh-rsa AAAAB3NzaC'
class FakeSecurityGroup(FakeBase):
id = '1'
name = 'name'
class FakeNeutronManager(object):
def __init__(self):
self.groups = [{u'description': u'default',
u'id': u'secgorup1',
u'name': u'default',
u'security_group_rules': [
{u'direction': u'ingress',
u'ethertype': u'IPv4',
u'id': u'secgroup-rule1',
u'port_range_max': 65535,
u'port_range_min': 1,
u'protocol': u'tcp',
u'remote_group_id': None,
u'remote_ip_prefix': u'0.0.0.0/0',
u'security_group_id': u'secgorup1',
u'tenant_id': u'tenant1'},
],
u'tenant_id': u'tenant1'}]
self.routers = [
{u'admin_state_up': True,
u'external_gateway_info': {u'enable_snat': True,
u'network_id': u'network3'},
u'id': u'router1',
u'name': u'gw-internal-a',
u'routes': [],
u'status': u'ACTIVE',
u'tenant_id': u'tenant1'},
]
self.ports = [{u'admin_state_up': True,
u'allowed_address_pairs': [],
u'binding:vnic_type': u'normal',
u'device_id': u'router1',
u'device_owner': u'network:router_interface',
u'extra_dhcp_opts': [],
u'fixed_ips': [{u'ip_address': u'192.168.203.1',
u'subnet_id': u'subnet3'}],
u'id': u'port1',
u'mac_address': u'fa:16:3e:fe:c1:b3',
u'name': u'',
u'network_id': u'network1',
u'security_groups': [],
u'status': u'ACTIVE',
u'tenant_id': u'tenant1'},
{u'admin_state_up': True,
u'allowed_address_pairs': [],
u'binding:vnic_type': u'normal',
u'device_id': u'server3',
u'device_owner': u'compute:nova',
u'extra_dhcp_opts': [],
u'fixed_ips': [{u'ip_address': u'192.168.203.5',
u'subnet_id': u'subnet3'}],
u'id': u'port2',
u'mac_address': u'fa:16:3e:e4:44:7b',
u'name': u'',
u'network_id': u'network1',
u'security_groups': [u'secgorup1'],
u'status': u'ACTIVE',
u'tenant_id': u'tenant1'},
{u'admin_state_up': True,
u'allowed_address_pairs': [],
u'binding:vnic_type': u'normal',
u'device_id': u'server2',
u'device_owner': u'compute:nova',
u'extra_dhcp_opts': [],
u'fixed_ips': [{u'ip_address': u'192.168.203.4',
u'subnet_id': u'subnet3'}],
u'id': u'port3',
u'mac_address': u'fa:16:3e:e8:e4:e2',
u'name': u'',
u'network_id': u'network1',
u'security_groups': [u'secgorup1'],
u'status': u'ACTIVE',
u'tenant_id': u'tenant1'},
{u'admin_state_up': True,
u'allowed_address_pairs': [],
u'binding:vnic_type': u'normal',
u'device_id': u'dhcp1-network1',
u'device_owner': u'network:dhcp',
u'extra_dhcp_opts': [],
u'fixed_ips': [{u'ip_address': u'192.168.203.3',
u'subnet_id': u'subnet3'},
{u'ip_address': u'192.168.204.2',
u'subnet_id': u'subnet4'}],
u'id': u'port4',
u'mac_address': u'fa:16:3e:af:86:30',
u'name': u'',
u'network_id': u'network1',
u'security_groups': [],
u'status': u'ACTIVE',
u'tenant_id': u'tenant1'},
{u'admin_state_up': True,
u'allowed_address_pairs': [],
u'binding:vnic_type': u'normal',
u'device_id': u'server1',
u'device_owner': u'compute:nova',
u'extra_dhcp_opts': [],
u'fixed_ips': [{u'ip_address': u'192.168.203.2',
u'subnet_id': u'subnet3'}],
u'id': u'port6',
u'mac_address': u'fa:16:3e:b0:9a:e2',
u'name': u'',
u'network_id': u'network1',
u'security_groups': [u'secgorup1'],
u'status': u'ACTIVE',
u'tenant_id': u'tenant1'}
]
self.subnets = [{u'allocation_pools': [
{u'end': u'172.19.0.254', u'start': u'172.19.0.2'}],
u'cidr': u'172.19.0.0/24',
u'dns_nameservers': [],
u'enable_dhcp': True,
u'gateway_ip': u'172.19.0.1',
u'host_routes': [],
u'id': u'subnet1',
u'ip_version': 4,
u'name': u'storage',
u'network_id': u'network2',
u'tenant_id': u'tenant1'},
{u'allocation_pools': [
{u'end': u'10.8.8.200',
u'start': u'10.8.8.100'}],
u'cidr': u'10.8.8.0/24',
u'dns_nameservers': [],
u'enable_dhcp': False,
u'gateway_ip': u'10.8.8.254',
u'host_routes': [],
u'id': u'subnet2',
u'ip_version': 4,
u'name': u'ext-subnet',
u'network_id': u'network3',
u'tenant_id': u'tenant1'},
{u'allocation_pools': [{u'end': u'192.168.203.254',
u'start': u'192.168.203.2'}],
u'cidr': u'192.168.203.0/24',
u'dns_nameservers': [],
u'enable_dhcp': True,
u'gateway_ip': u'192.168.203.1',
u'host_routes': [],
u'id': u'subnet3',
u'ip_version': 4,
u'name': u'int-a-1',
u'network_id': u'network1',
u'tenant_id': u'tenant1'},
{u'allocation_pools': [{u'end': u'192.168.204.254',
u'start': u'192.168.204.2'}],
u'cidr': u'192.168.204.0/24',
u'dns_nameservers': [],
u'enable_dhcp': True,
u'gateway_ip': u'192.168.204.1',
u'host_routes': [],
u'id': u'subnet4',
u'ip_version': 4,
u'name': u'int-a-2',
u'network_id': u'network1',
u'tenant_id': u'tenant1'}]
self.networks = [{u'admin_state_up': True,
u'id': u'network1',
u'name': u'internal',
u'router:external': False,
u'shared': False,
u'status': u'ACTIVE',
u'subnets': [u'subnet3',
u'subnet4'],
u'tenant_id': u'tenant1'},
{u'admin_state_up': True,
u'id': u'network2',
u'name': u'storage',
u'router:external': False,
u'shared': False,
u'status': u'ACTIVE',
u'subnets': [u'subnet1'],
u'tenant_id': u'tenant1'},
{u'admin_state_up': True,
u'id': u'network3',
u'name': u'ext-net',
u'router:external': True,
u'shared': True,
u'status': u'ACTIVE',
u'subnets': [u'subnet2'],
u'tenant_id': u'tenant1'}]
self.floatingips = [{u'fixed_ip_address': None,
u'floating_ip_address': u'10.8.8.102',
u'floating_network_id': u'network3',
u'id': u'floating1',
u'port_id': None,
u'router_id': None,
u'status': u'DOWN',
u'tenant_id': u'tenant1'},
{u'fixed_ip_address': None,
u'floating_ip_address': u'10.8.8.101',
u'floating_network_id': u'network3',
u'id': u'floating2',
u'port_id': None,
u'router_id': None,
u'status': u'DOWN',
u'tenant_id': u'tenant1'},
{u'fixed_ip_address': u'192.168.203.4',
u'floating_ip_address': u'10.8.8.168',
u'floating_network_id': u'network3',
u'id': u'floating3',
u'port_id': u'port3',
u'router_id': u'router1',
u'status': u'ACTIVE',
u'tenant_id': u'tenant1'},
{u'fixed_ip_address': None,
u'floating_ip_address': u'10.8.8.118',
u'floating_network_id': u'network3',
u'id': u'floating4',
u'port_id': None,
u'router_id': None,
u'status': u'DOWN',
u'tenant_id': u'tenant1'}]
def subnet_list(self):
return self.subnets
def network_list(self):
return self.networks
def port_list(self):
return self.ports
def router_list(self):
return self.routers
def router_interfaces_list(self, router):
return [port for port in self.ports
if port['device_id'] == router['id']]
def secgroup_list(self):
return self.groups
def floatingip_list(self):
return self.floatingips
class FakeNovaManager(object):
def __init__(self):
self.servers = [FakeServer('server1'),
FakeServer('server2'),
FakeServer('server3')]
self.servergroups = []
self.flavors = [FakeFlavor(id='2', name='m1.small')]
self.groups = {}
self.keypairs = [FakeKeypair(name='testkey',
public_key='ssh-rsa XXXX')]
def keypair_list(self):
return self.keypairs
def flavor_list(self):
return self.flavors
def server_list(self):
return self.servers
def server_security_group_list(self, server):
return self.groups.get(server.name, [])
def servergroup_list(self):
return self.servergroups
class FakeCinderManager(object):
def __init__(self):
self.volumes = [FakeVolume(), ]
def volume_list(self):
return self.volumes
class ResourceTestCase(base.TestCase):
def test_template_resource(self):
resource = flame.Resource('my-name',
'my-type',
properties='my-properties')
expected = {
'my-name': {
'type': 'my-type',
'properties': 'my-properties',
}
}
self.assertEqual(expected, resource.template_resource)
class BaseTestCase(base.TestCase):
def setUp(self):
super(BaseTestCase, self).setUp()
self.patch_neutron = mock.patch('flameclient.managers.NeutronManager')
self.mock_neutron = self.patch_neutron.start()
self.patch_nova = mock.patch('flameclient.managers.NovaManager')
self.mock_nova = self.patch_nova.start()
self.patch_cinder = mock.patch('flameclient.managers.CinderManager')
self.mock_cinder = self.patch_cinder.start()
def tearDown(self):
self.mock_neutron.stop()
self.mock_nova.stop()
self.mock_cinder.stop()
super(BaseTestCase, self).tearDown()
def get_generator(self, exclude_servers, exclude_volumes,
exclude_keypairs, generate_data, extract_ports):
generator = flame.TemplateGenerator('x', 'x', 'x', 'x', True,
'publicURL')
generator.extract_vm_details(exclude_servers, exclude_volumes,
exclude_keypairs, generate_data,
extract_ports)
return generator
def check_stackdata(self, resources, expected_resources):
merged_resources = {}
for resource in resources:
merged_resources.update(resource.stack_resource)
self.assertEqual(expected_resources, merged_resources)
def check_template(self, resources, expected_resources,
expected_parameters=None):
expected_parameters = expected_parameters or {}
merged_resources = {}
merged_parameters = {}
for resource in resources:
merged_resources.update(resource.template_resource)
merged_parameters.update(resource.template_parameter)
self.assertEqual(expected_resources, merged_resources)
self.assertEqual(expected_parameters, merged_parameters)
class StackDataTests(BaseTestCase):
def setUp(self):
super(StackDataTests, self).setUp()
self.mock_neutron.return_value = FakeNeutronManager()
self.mock_nova.return_value = FakeNovaManager()
self.mock_cinder.return_value = FakeCinderManager()
def test_routers_presents(self):
generator = self.get_generator(False, False, False, True, True)
extraction = generator._extract_routers()
routers = {r.name: r for r in extraction}
self.assertIn('router_0', routers)
def test_routers_resource_names(self):
generator = self.get_generator(False, False, False, True, True)
generator_output = generator._extract_routers()
routers = (res for res in generator_output
if res.type == "OS::Neutron::Router")
for n, router in enumerate(routers):
assert(router.name.startswith("router_"))
def test_ports_presents(self):
generator = self.get_generator(False, False, False, True, True)
extraction = generator._extract_ports()
ports = {r.name: r for r in extraction}
self.assertIn('port_1', ports)
self.assertIn('port_2', ports)
def test_ports_resource_names_types(self):
generator = self.get_generator(False, False, False, True, True)
extraction = generator._extract_ports()
for n, port in enumerate(extraction):
props = port.properties
assert(extraction[0].name.startswith("port_"))
self.assertEqual("OS::Neutron::Port", port.type)
self.assertIsInstance(props['admin_state_up'], bool)
self.assertIsInstance(props['security_groups'], list)
assert(props['device_owner'].startswith("compute:"))
def test_port_fixed_ip(self):
reference = [{'ip_address': '192.168.203.2',
'subnet_id': {'get_resource': 'subnet_2'}}]
generator = self.get_generator(False, False, False, True, True)
extraction = generator._extract_ports()
# Get the right port for the test
port = next((p for p in extraction if
p.properties['mac_address'] == 'fa:16:3e:b0:9a:e2'))
props = port.properties
self.assertIsInstance(props['fixed_ips'], list)
fixed_ips = props['fixed_ips']
for ref in reference:
self.assertIn(ref, fixed_ips)
def test_servers_ports_assignations(self):
generator = self.get_generator(False, False, False, True, True)
extraction = generator._extract_servers()
used_ports = []
for n, server in enumerate(extraction):
props = server.properties
self.assertIsInstance(props['networks'], list)
for network in props['networks']:
port = network['port']['get_resource']
assert(port.startswith("port_"))
# Port has not been used by another server
self.assertNotIn(port, used_ports)
used_ports.append(port)
def test_floating_association(self):
generator = self.get_generator(False, False, False, True, True)
extraction = generator._extract_floating()
associations = (res for res in extraction
if res.type == "OS::Neutron::FloatingIPAssociation")
for association in associations:
props = association.properties
assert(props['floatingip_id']['get_resource'].
startswith('floatingip_'))
assert(props['port_id']['get_resource'].
startswith('port_'))
class GenerationTests(BaseTestCase):
resource_ref = set(['floatingip_association_2',
'subnet_2', 'subnet_3', 'subnet_0',
'port_2', 'port_1', 'port_4',
'server_2', 'server_1', 'server_0',
'router_0',
'router_0_interface_0',
'router_0_gateway',
'key_0',
'network_0', 'network_1',
'floatingip_0', 'floatingip_1',
'floatingip_2', 'floatingip_3',
'volume_0'])
params_ref = set(['volume_0_volume_type',
'external_network_for_floating_ip_3',
'external_network_for_floating_ip_2',
'external_network_for_floating_ip_1',
'external_network_for_floating_ip_0',
'port_4_default_security_group',
'port_1_default_security_group',
'port_2_default_security_group',
'router_0_external_network',
'server_1_image',
'server_1_flavor',
'server_1_key',
'server_2_image',
'server_2_flavor',
'server_2_key',
'server_0_image',
'server_0_flavor',
'server_0_key'])
data_ref = set(['floatingip_0', 'floatingip_1', 'floatingip_2',
'floatingip_3',
'floatingip_association_2',
'key_0',
'network_0', 'network_1',
'port_1', 'port_2', 'port_4',
'router_0',
'router_0_gateway',
'router_0_interface_0',
'server_0', 'server_1', 'server_2',
'subnet_0', 'subnet_2', 'subnet_3',
'volume_0'])
def filter_set(self, filtered_set, exclude):
excluded_set = set()
for exc in exclude:
excluded_set.update(
set([e for e in filtered_set if re.search(exc, e)])
)
return filtered_set.difference(excluded_set)
def setUp(self):
super(GenerationTests, self).setUp()
self.mock_neutron.return_value = FakeNeutronManager()
self.mock_nova.return_value = FakeNovaManager()
self.mock_cinder.return_value = FakeCinderManager()
def test_generation(self):
exclusion_table = [
{'call_params': (False, False, False, True, True),
'resource_filter': [],
'params_filter': ['^server_\d+_key$'],
'data_filter': []},
# No server
{'call_params': (True, False, False, True, True),
'resource_filter': ['^server'],
'params_filter': ['^server'],
'data_filter': ['^server']},
# No volumes
{'call_params': (False, True, False, True, True),
'resource_filter': ['^volume'],
'params_filter': [r'^volume_\d+_volume_type$',
'^server_\d+_key$'],
'data_filter': ['^volume']},
# No keys
{'call_params': (False, False, True, True, True),
'resource_filter': ['^key_\d+$'],
'params_filter': [],
'data_filter': ['^key', 'server_\d+_key']},
# No ports
{'call_params': (False, False, False, True, False),
'resource_filter': ['^port_\d+$'],
'params_filter': ['^port_\d+_default_security_group$',
'server_\d+_key$'],
'data_filter': ['^port_\d+', '^floatingip_association_\d+$']},
]
for exclusion in exclusion_table:
generator = self.get_generator(*exclusion['call_params'])
resource_ref = self.filter_set(self.resource_ref,
exclusion['resource_filter'])
params_ref = self.filter_set(self.params_ref,
exclusion['params_filter'])
data_ref = self.filter_set(self.data_ref,
exclusion['data_filter'])
generator.extract_data()
# All the resources, params and datas are present
self.assertEqual(resource_ref,
set(generator.template['resources'].keys()),
"Called with : %r" % (exclusion['call_params'],))
self.assertEqual(params_ref,
set(generator.template['parameters'].keys()),
"Called with : %r" % (exclusion['call_params'],))
self.assertEqual(data_ref,
set(generator.stack_data['resources'].keys()),
"Called with : %r" % (exclusion['call_params'],))
def test_floating_association_data(self):
generator = self.get_generator(False, False, False, True, True)
generator.extract_data()
# Look for floating ips
assoc_name = 'floatingip_association_2'
association_data = generator.stack_data['resources'][assoc_name]
reference = {'action': 'CREATE',
'metadata': {},
'name': 'floatingip_association_2',
'resource_data': {},
'resource_id': u'floating3:port3',
'status': 'COMPLETE',
'type': 'OS::Neutron::FloatingIPAssociation'}
self.assertEqual(reference, association_data)
def test_port_data(self):
generator = self.get_generator(False, False, False, True, True)
generator.extract_data()
# Look for floating ips
assoc_name = 'port_2'
association_data = generator.stack_data['resources'][assoc_name]
reference = {'action': 'CREATE',
'metadata': {},
'name': 'port_2',
'resource_data': {},
'resource_id': u'port3',
'status': 'COMPLETE',
'type': 'OS::Neutron::Port'}
self.assertEqual(reference, association_data)

View File

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from flameclient.tests import unittest
from flameclient import utils
class TestUtils(unittest.TestCase):
def test_camel_to_snake(self):
for inp, outp in (
('camelcase', 'camelcase'),
('Camelcase', 'camelcase'),
('camelCase', 'camel_case'),
('CamelCase', 'camel_case'),
('camelCCase', 'camel_c_case'),
('CCamelCase', 'c_camel_case'),
('CCCCamelCase', 'ccc_camel_case'),
('CCCcamelCase', 'cc_ccamel_case'),
('Camel123case', 'camel_123case'),
('Camel123Case', 'camel_123_case'),
):
self.assertEqual(utils.camel_to_snake(inp), outp)
def test_format_option(self):
for option, formated in (
('--foo-bar', 'foo_bar'),
('--this-is-a-long-option', 'this_is_a_long_option')
):
self.assertEqual(
formated, utils.format_option(option)
)
def test_format_option_kwargs(self):
self.assertEqual(
{'foo_bar': 'value'}, utils.format_option_kwargs(
{'--foo-bar': 'value'}
)
)

View File

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import six
import openstack
from flameclient.tests import mock
from flameclient.utils import get_deep_attr
from flameclient.utils import munchify
def get_mocked_openstackcloud():
from flameclient.tests.fixtures import openstackcloud
cloud = mock.Mock(spec_set=openstack.connection.Connection)
# for each self.conn.`fixture_module.NAME`() call we will return
# fixture_module.FIXTURES
for fixture_module in six.itervalues(openstackcloud.FIXTURES):
get_deep_attr(
cloud, fixture_module.NAME
).return_value = munchify(fixture_module.FIXTURES)
return cloud

363
flameclient/utils.py Normal file
View File

@ -0,0 +1,363 @@
# -*- coding: utf-8 -*-
# This software is released under the MIT License.
#
# Copyright (c) 2018 Orange Cloud for Business / Cloudwatt
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import argparse
import re
from threading import Lock
import importlib
import os
import pkgutil
import sys
import munch
import pkg_resources
import six
from flameclient import collections_abc
CS2STR1 = re.compile('(.)([A-Z][a-z]+)')
CS2STR2 = re.compile('(.)([0-9]+)')
CS2STR3 = re.compile('([a-z0-9])([A-Z])')
REPLSTR = r'\1_\2'
def camel_to_snake(string):
"""Transform camel_case string to snake_case string"""
return CS2STR3.sub(
REPLSTR, CS2STR2.sub(REPLSTR, CS2STR1.sub(REPLSTR, string))
).lower()
def sys_path_to_module(path):
full_path = os.path.realpath(os.path.abspath(path))
path = full_path
while True:
if path in sys.path:
break
path = os.path.dirname(path)
if path == '/':
return ''
return path
def get_module_name_from_file(filename):
filename = os.path.realpath(os.path.abspath(filename))
sys_path = '%s/' % sys_path_to_module(filename)
package_path = filename.split(sys_path, 1)[1]
package_path = package_path.rsplit('.py', 1)[0]
return package_path.replace('/', '.')
def load_resource_modules(base_file_or_dir, exclude=tuple()):
"""Load packages in a directory
base_file_or_dir: the base file or dir where we will load packages.
prefix: the prefix to add to module names.
"""
modules = {}
if isinstance(exclude, six.string_types):
exclude = (exclude, )
full_path = os.path.realpath(os.path.abspath(base_file_or_dir))
if os.path.isdir(full_path):
pkg_dir = full_path
elif os.path.isfile(full_path):
pkg_dir = os.path.dirname(full_path)
else:
raise ImportError('Can not find "%s"' % full_path)
prefix = "%s." % get_module_name_from_file(pkg_dir)
for _, name, ispkg in pkgutil.iter_modules([pkg_dir], prefix):
# `name` has the form "foo.bar.plop"
# We do not want to load "foo.bar.test*" modules to not load unittest
# files.
# We also do not want to load files starting with an underscore.
if (
not name.split('.')[-1].startswith('test') and
not name.split('.')[-1].startswith('_') and
name not in exclude and
not ispkg
):
modules[name] = importlib.import_module(name)
return modules
def load_resource_entry_points(name='openstack_flame'):
entry_points = {}
for entry_point in pkg_resources.iter_entry_points(name):
entry_points[entry_point.name] = entry_point.load()
return entry_points
def munchify(obj, iterator_type=list, remunch=False):
"""Transforms an object into a `munch.Munch` object when possible.
The difference with `munch.munchify` is that when an object has a
`_to_munch` method, we call this method which was designed for it.
This method is useful when we have `openstack.resource.Resource` objects
because they have a `_to_munch` method.
The purpose is to have the same type of objects whether we use
`openstack.connection.Connection` methods or
`shade.openstackcloud.OpenStackCloud` specific methods.
`openstack.connection.Connection` methods return
`openstack.resource.Resource` subclasses and
`shade.openstackcloud.OpenStackCloud` methods return `munch.Munch` objects.
:param bool remunch: If True we return a deep copy of the object if it's
already a `munch.Munch` instance. By default we
return the `munch.Munch` object as is.
:param function iterator_type: If we have an iterator instead of an
iterable object we return a list by default.
You can change this behaviour by passing the
callable to the type you want (ex.: tuple).
This only affects Iterators and not
iterables for which we keep the same type.
You get an iterator when you have an
`openstack.connection.Connection` instance
and do e.g.: `conn.compute.servers()`.
So if you call
`munchify(conn.compute.servers())` you will
get the iterator_type (a list by default).
"""
# Check
# https://docs.python.org/2/library/collections.html#collections-abstract-base-classes # noqa
# or
# https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes # noqa
# To know in which order to test.
if not remunch and isinstance(obj, munch.Munch):
return obj
if hasattr(obj, '_to_munch'):
return obj._to_munch()
elif isinstance(obj, collections_abc.Mapping): # Test for `dict` likes.
# Mappings (dicts, OrderedDict, etc...) are iterable so it
# needs to be tested before iterable types.
return munch.Munch(
(key, munchify(value)) for key, value in six.iteritems(obj)
)
elif isinstance(obj, six.string_types):
# Strings are iterable so this needs to be tested before the
# iterable types.
return obj
elif isinstance(obj, collections_abc.Iterator):
# `Iterator` is a subclass of `Iterable` so we need to test it first.
return iterator_type(munchify(elt) for elt in obj)
elif isinstance(obj, collections_abc.Iterable):
# At last, check if we have an iterable object.
return type(obj)(munchify(elt) for elt in obj)
else:
return munch.munchify(obj)
def data_list_to_dict(data_list, enum=True):
data_dict = munch.Munch()
for num, data in enumerate(data_list):
data_dict[data.id] = munchify(data)
if enum:
data_dict[data.id].enum = num
return data_dict
def clean_dict(obj, clean_list=True, super_clean=False, iterator_type=list):
"""Returns a dict copy with only values which are not None
:param bool clean_list: whether to suppress or not None values from lists
:param bool super_clean: whether to suppress or not values which evaluate
to False (not only None).
:param function iterator_type: cast function for iterator types.
"""
if isinstance(obj, six.string_types):
return obj
elif isinstance(obj, collections_abc.Mapping):
if super_clean:
return type(obj)(
(key, clean_dict(
value, clean_list=clean_list, super_clean=super_clean))
for key, value in six.iteritems(obj) if value
)
return type(obj)(
(key, clean_dict(
value, clean_list=clean_list, super_clean=super_clean))
for key, value in six.iteritems(obj)if value is not None
)
elif isinstance(obj, collections_abc.Iterator):
if super_clean:
return iterator_type(
clean_dict(elt, clean_list=clean_list, super_clean=super_clean)
for elt in obj if elt or not clean_list
)
return iterator_type(
clean_dict(elt, clean_list=clean_list, super_clean=super_clean)
for elt in obj if elt is not None or not clean_list
)
elif isinstance(obj, collections_abc.Iterable):
# At last, check if we have an iterable object.
if super_clean:
return type(obj)(
clean_dict(elt, clean_list=clean_list, super_clean=super_clean)
for elt in obj if elt or not clean_list
)
return type(obj)(
clean_dict(elt, clean_list=clean_list, super_clean=super_clean)
for elt in obj if elt is not None or not clean_list
)
else:
return obj
def format_option(option_str):
option_str = option_str.lstrip('-')
option_str = option_str.replace('-', '_')
return option_str
def format_option_kwargs(kwargs):
"""Format a dictionary with option names to dict keys.
This will allow further keyword arguments passing of argparse options.
example:
change `{'--foo-bar': 'value'}` to `{'foo_bar': 'value'}` etc...
"""
return {
format_option(key): value for key, value in six.iteritems(kwargs)
}
def dict_to_options(kwargs):
if kwargs is not None:
if isinstance(kwargs, argparse.Namespace):
return kwargs
return argparse.Namespace(**format_option_kwargs(kwargs))
return None
def rename_os_kwargs(kwargs, clean=False):
"""Clean Openstack kwargs from the 'os_' prefix.
envvars return 'os_username', 'os_auth_url', etc... variables.
we want to remove the 'os_' prefix
:param dict kwargs: the dictionary to clean
:param bool clean: If true, the returned dictionary will not have keys
with empty values.
"""
new_kwargs = {}
for key, value in six.iteritems(kwargs):
if value or not clean:
if key.startswith('os_'):
new_kwargs[key.split('os_')[1]] = value
else:
new_kwargs[key] = value
return new_kwargs
def rename_os_options(options, clean=False):
"""Clean Openstack envvars from the 'os_' prefix.
envvars return 'os_username', 'os_auth_url', etc... variables.
we want to remove the 'os_' prefix
:param argparse.Namespace options: the options to clean
:param bool clean: If true, the returned options will not have
attributes with empty values.
"""
kwargs = vars(options)
renamed_kwargs = rename_os_kwargs(kwargs, clean=clean)
return argparse.Namespace(**renamed_kwargs)
def hash_func_call(func, *args, **kwargs):
"""hash a function call"""
# kwargs is a dict, and dicts are not hashable.
kwargs_tuple = tuple((key, value) for key, value in six.iteritems(kwargs))
return hash((func, args, kwargs_tuple))
def get_deep_attr(obj, value):
"""Get deep attribute
example, `getattr(some_object, 'attr.subattr')` does not work.
With get_deep_attr it works.
"""
subelts = value.split('.', 1)
if len(subelts) == 1:
return getattr(obj, value)
else:
return get_deep_attr(getattr(obj, subelts[0]), subelts[1])
def memoized_property(func):
"""Decorator to set properties which will be computed only once
"""
attr_name = '__%s' % func.__name__
lock = Lock()
class FixedProperty(object):
def __get__(self, instance, owner):
if instance:
with lock:
try:
return getattr(instance, attr_name)
except AttributeError:
result = func(instance)
setattr(instance, attr_name, result)
return result
return self
def __set__(self, instance, value):
with lock:
setattr(instance, attr_name, value)
def __delete__(self, instance):
with lock:
delattr(instance, attr_name)
return FixedProperty()
class ClassProperty(classmethod):
def __get__(self, instance, owner):
return self.__func__(owner)
def __set__(self, instance, value):
setattr(instance, self.__func__.__name__, value)
def __delete__(self, instance):
delattr(instance, self.__func__)

110
pylintrc Normal file
View File

@ -0,0 +1,110 @@
[MASTER]
profile=no
persistent=yes
ignore=migrations
[MESSAGES CONTROL]
# C0103 Invalid %s name "%s"
# C0111 Missing docstring
# C0302 Too many lines in module
# C0330 Wrong hanging indentation before block (We let pep8 check this)
# E0611 No name %r in module %r
# E1101 %s %r has no %r member
# E1102 %s is not callable
# E1133 Non-iterable value %s is used in an iterating context # (caused by flameclient.utils.memoized_property)
# E1135 Value '%s' doesn't support membership test # (caused by flameclient.utils.memoized_property)
# E1136 %s is unsubscriptable # (caused by flameclient.utils.fixed_property)
# F0401 Unable to import %s
# I0011 Warning locally suppressed using disable-msg
# I0012 Warning locally suppressed using disable-msg
# R0123 Comparison to literal
# R0201 Method could be a function
# R0901 Too many ancestors
# R0902 Too many instance attributes
# R0904 Too many public methods
# R0911 Too many return statements
# R0912 Too many branches
# R0914 Too many local variables
# R0915 Too many statements
# R1705 Unnecessary "else" after "return"
# R1710 inconsistent-return-statements
# W0142 Used * or * magic* Used when a function or method is called using *args or **kwargs to dispatch arguments.
# W0212 Access to a protected member %s of a client class
# W0223 Method %r is abstract in class %r but is not overridden
# W0232 Class has no __init__ method Used when a class has no __init__ method, neither its parent classes.
# W0403 Relative import '%s', should be '%s'
# W0511 Used when a warning note as FIXME or XXX is detected.
# W0613 Unused argument %r Used when a function or method argument is not used.
# W0702 No exception's type specified Used when an except clause doesn't specify exceptions type to catch.
# W0704 Except doesn't do anything Used when an except clause does nothing but "pass" and there is no "else" clause
# W1113 keyword-arg-before-vararg
# example:
# disable=C0111,I0011,I0012,R0201,W0142,W0212,W0232,W0613,W0702,W0704
# or:
# disable=I0011,I0012,W0142,W0212,W0232
disable=C0103,C0111,C0302,C0330,E0611,E1101,E1102,E1133,E1135,E1136,I0011,I0012,R0123,R0901,R0902,R0904,R0911,R0912,R0914,R0915,R1705,R1710,W0142,W0212,W0223,W0403,W0511,W0613,W0702,W0704,W1113
[REPORTS]
include-ids=yes
output-format=parseable
#reports=yes
[BASIC]
#no-docstring-rgx=__.*__|_.*
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__)|logger|register|urlpatterns)$
method-rgx=([a-z_][a-z0-9_]{2,30}|setUp|tearDown|test_[a-z0-9_]{2,60}|assert[a-zA-Z0-9]{2,30})$
#good-names=_,i,j,k,e,v,db,qs,pk
good-names=_,i,j,k,e,v,by,fp,td,tr
[TYPECHECK]
ignore-mixin-members=yes
#ignored-classes=SQLObject,WSGIRequest
#zope=no
#generated-members=objects,DoesNotExist,id,pk,_meta,base_fields,context
[VARIABLES]
#init-import=no
#dummy-variables-rgx=_|dummy
#additional-builtins=
[SIMILARITIES]
min-similarity-lines=6
#ignore-comments=yes
#ignore-docstrings=yes
ignore-imports=yes
[MISCELLANEOUS]
notes=FIXME,XXX,TODO
[FORMAT]
max-line-length=1000
max-module-lines=500
indent-string=' '
[CLASSES]
#defining-attr-methods=__init__,__new__,setUp
[DESIGN]
max-args=10
max-locals=15
max-returns=6
max-branchs=12
max-statements=50
max-parents=7
max-attributes=7
min-public-methods=0
max-public-methods=50
[IMPORTS]
#deprecated-modules=regsub,TERMIOS,Bastion,rexec

View File

@ -1,10 +1,20 @@
pbr>=1.6
Babel>=1.3
futures
netaddr>=0.7.12,!=0.7.16
python-keystoneclient
python-neutronclient
python-novaclient
python-cinderclient
PyYAML
certifi==2018.8.24
futures>=3.1.1,<=3.2.0
keystoneauth1==3.11.0
ndg-httpsclient==0.5.1
netaddr==0.7.19
openstacksdk==0.17.2
os-client-config==1.31.2
pbr==4.2.0
pyasn1==0.4.4
pyOpenSSL==18.0.0
python-cinderclient==4.0.1
python-glanceclient==2.12.1
python-heatclient==1.16.1
python-keystoneclient==3.17.0
python-neutronclient==6.10.0
python-novaclient==11.0.0
python-openstackclient==3.16.1
python-swiftclient==3.6.0
PyYAML==3.13
shade==1.29.0

View File

@ -3,7 +3,7 @@ name = python-flameclient
summary = Automatic Heat template generation
description-file =
README.rst
author = CloudWatt
author = Orange Cloud for Business / CloudWatt
author-email = info@cloudwatt.com
home-page = http://www.cloudwatt.com/
classifier =

View File

@ -1,13 +1,17 @@
hacking>=0.10.2,<0.11
futures
coverage>=3.6
discover
mock>=1.2
fixtures>=1.3.1
python-subunit
sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
oslosphinx
testrepository>=0.0.18
testscenarios>=0.4
testtools>=1.4.0
coverage==4.5.1
discover==0.4.0
fixtures==3.0.0
hacking==1.1.0
mccabe<0.6,>=0.2.1
mock==2.0.0
oslosphinx==4.18.0
os-testr==1.0.0
pycodestyle==2.0.0
pylint==1.9.3
python-subunit==1.3.0
sphinx==1.8.0
stestr==2.1.1
testrepository==0.0.20
testscenarios==0.5.0
testtools==2.3.0
tox==3.4.0

11
tox.ini
View File

@ -1,11 +1,11 @@
[tox]
minversion = 1.6
envlist = py33,py27,pypy,pep8
envlist = py37,py36,py35,py34,py27,pypy,pep8,pylint
skipsdist = True
[testenv]
usedevelop = True
install_command = pip install -U {opts} {packages}
install_command = pip install -c {toxinidir}/upper-constraints.txt -U {opts} {packages}
setenv =
VIRTUAL_ENV={envdir}
deps = -r{toxinidir}/requirements.txt
@ -13,7 +13,10 @@ deps = -r{toxinidir}/requirements.txt
commands = python setup.py testr --slowest --testr-args='{posargs}'
[testenv:pep8]
commands = flake8
commands = python -m flake8 flameclient
[testenv:pylint]
commands = python -m pylint --rcfile=pylintrc --reports=yes flameclient
[testenv:venv]
commands = {posargs}
@ -30,4 +33,4 @@ commands = python setup.py build_sphinx
show-source = True
ignore = H803
builtins = _
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,flameclient/tests/fixtures/openstackcloud/*

552
upper-constraints.txt Normal file
View File

@ -0,0 +1,552 @@
ntlm-auth===1.2.0
voluptuous===0.11.5
chardet===3.0.4
rsa===3.4.2
restructuredtext-lint===1.1.3
netmiko===2.2.2
instack-undercloud===9.3.0
PasteDeploy===1.5.2
typing===3.6.6
python-saharaclient===2.0.0
python-hnvclient===0.1.0
Routes===2.4.1
rtslib-fb===2.1.66
XStatic-Angular-Bootstrap===2.2.0.0
paunch===3.2.0
WebOb===1.8.2
sphinxcontrib-actdiag===0.8.5
docopt===0.6.2
pecan===1.3.2
ryu===4.27
os-api-ref===1.5.0
python-ldap===3.1.0
oslo.concurrency===3.27.0
websocket-client===0.53.0
osprofiler===2.3.0
tabulate===0.8.2
python-ironic-inspector-client===3.3.0
lxml===4.2.5
python-kingbirdclient===0.2.1
setproctitle===1.1.10
pytest===3.8.0
python-etcd===0.4.5
raven===6.9.0
cursive===0.2.2
oslo.service===1.32.0
django-appconf===1.0.2
pykerberos===1.2.1
certifi===2018.8.24
sphinxcontrib-nwdiag===0.9.5
requests-aws===0.1.8
alabaster===0.7.11
pbr===4.2.0
munch===2.3.2
attrs===18.2.0
microversion-parse===0.2.1
Pint===0.8.1
oslo.i18n===3.21.0
jsonpath-rw-ext===1.1.3
python-mistralclient===3.7.0
oslo.context===2.21.0
python-senlinclient===1.8.0
rcssmin===1.0.6
pycadf===2.8.0
grpcio===1.15.0
skydive-client===0.4.5
pysendfile===2.0.1
fixtures===3.0.0
neutron-lib===1.18.0
XStatic-FileSaver===1.3.2.0
pystache===0.5.4
XStatic-Font-Awesome===4.7.0.0
nose===1.3.7
nosehtmloutput===0.0.5
waitress===1.1.0
os-refresh-config===9.1.0
pysnmp===4.4.6
sphinxcontrib-websupport===1.1.0
Mako===1.0.7
XStatic-angular-ui-router===0.3.1.2
pyScss===1.3.4
XStatic-jQuery===1.10.2.1
jsonmodels===2.3
ddt===1.2.0
pyserial===3.4
ipaddress===1.0.22;python_version=='2.7'
python-freezerclient===1.7.0
os-xenapi===0.3.4
python-vitrageclient===2.3.0
nosexcover===1.0.11
krest===1.3.1
psycopg2===2.7.5
networkx===2.1
bashate===0.6.0
XStatic-Angular===1.5.8.0
pyngus===2.2.4
Pillow===5.2.0
zuul-sphinx===0.2.5
python-mimeparse===1.6.0
tripleo-common===9.3.0
Tempita===0.5.2
ply===3.11
requests-toolbelt===0.8.0
simplejson===3.16.0
suds-jurko===0.6
python-swiftclient===3.6.0
pyOpenSSL===18.0.0
monasca-common===2.11.0
scipy===1.1.0
rsd-lib===0.2.2
XStatic-Jasmine===2.4.1.1
python-glanceclient===2.12.1
pyinotify===0.9.6
debtcollector===1.20.0
requests-unixsocket===0.1.5
asn1crypto===0.24.0
croniter===0.3.25
python-watcherclient===2.1.0
MarkupSafe===1.0
pypowervm===1.1.18
doc8===0.8.0
pymongo===3.7.1
sqlparse===0.2.4
oslotest===3.6.0
jsonpointer===2.0
defusedxml===0.5.0
netaddr===0.7.19
pyghmi===1.2.14
sphinxcontrib-blockdiag===1.5.5
thrift===0.11.0
gnocchiclient===7.0.5
wcwidth===0.1.7
sphinxcontrib.datatemplates===0.1.0
jsonpath-rw===1.4.0
prettytable===0.7.2
vine===1.1.4
taskflow===3.2.0
traceback2===1.4.0
semantic-version===2.6.0
virtualbmc===1.4.0
deprecation===2.0.5
SQLAlchemy===1.2.11
pyroute2===0.5.2
google-auth===1.5.1
kazoo===2.5.0
XStatic-roboto-fontface===0.5.0.0
pyudev===0.21.0
eventlet===0.24.1
openstack-doc-tools===1.8.0
frozendict===1.2
oslo.messaging===8.1.0
jira===2.0.0
extras===1.0.0
PyJWT===1.6.4
zVMCloudConnector===1.2.4
paramiko===2.4.1
reno===2.11.0
unicodecsv===0.14.1;python_version=='2.7'
imagesize===1.1.0
pydot===1.2.4
pathlib===1.0.1;python_version=='2.7'
urllib3===1.23
graphviz===0.9
PyKMIP===0.8.0
whereto===0.4.0
python-subunit===1.3.0
tornado===4.5.3
pycparser===2.18
mock===2.0.0
PyYAML===3.13
beautifulsoup4===4.6.3
os-net-config===9.2.0
ovs===2.9.2
cryptography===2.3.1
adal===1.1.0
backports.ssl-match-hostname===3.5.0.1;python_version=='2.7'
openstack-release-test===1.1.0
pylxd===2.2.7
ruamel.ordereddict===0.4.13;python_version=='2.7'
pycryptodomex===3.6.6
anyjson===0.3.3
requests-mock===1.5.2
os-apply-config===9.1.0
oslosphinx===4.18.0
mox3===0.26.0
gunicorn===19.9.0
textfsm===0.4.1
unittest2===1.1.0
django-compressor===2.2
libvirt-python===4.6.0
python-zunclient===2.1.0
asyncio===3.4.3;python_version=='3.4'
asyncio===3.4.3;python_version=='3.5'
asyncio===3.4.3;python_version=='3.6'
tzlocal===1.5.1
python-novaclient===11.0.0
bcrypt===3.1.4
fixtures-git===0.1.0
os-client-config===1.31.2
XStatic-Angular-Gettext===2.3.8.0
XStatic-Hogan===2.0.0.2
XStatic-objectpath===1.2.1.0
python-manilaclient===1.24.1
requests===2.19.1
snowballstemmer===1.2.1
Jinja2===2.10
XStatic-Bootstrap-SCSS===3.3.7.1
pyzabbix===0.7.4
ptyprocess===0.6.0
threadloop===1.0.2
amqp===2.3.2
ruamel.yaml===0.15.66
websockify===0.8.0
XStatic-JQuery.quicksearch===2.0.3.1
mpmath===1.0.0
django-debreach===1.5.2
sphinx-feature-classification===0.3.0
XStatic-JQuery-Migrate===1.2.1.1
appdirs===1.4.3
tinyrpc===0.9.3
google-auth-httplib2===0.0.3
Flask-SQLAlchemy===2.3.2
daiquiri===1.5.0
influxdb===5.1.0
funcparserlib===0.3.6
passlib===1.7.1
dib-utils===0.0.11
cliff===2.13.0
os-brick===2.5.3
ansible-runner===1.1.1
trollius===2.2;python_version=='2.7'
scp===0.11.0
python-zaqarclient===1.10.0
funcsigs===1.0.2;python_version=='2.7'
zhmcclient===0.19.0
lockfile===0.12.2
dnspython3===1.15.0;python_version=='3.4'
dnspython3===1.15.0;python_version=='3.5'
dnspython3===1.15.0;python_version=='3.6'
ldappool===2.3.0
termcolor===1.1.0
hiredis===0.2.0
google-api-python-client===1.7.4
castellan===0.19.0
oslo.versionedobjects===1.33.3
webcolors===1.8.1
aodhclient===1.1.1
autobahn===18.9.2
SQLAlchemy-Utils===0.33.4
pluggy===0.7.1
coverage===4.5.1
freezegun===0.3.10
python-pytun===2.2.1
pyperclip===1.6.4
cassandra-driver===3.15.1
mox===0.5.3
XStatic-Angular-Schema-Form===0.8.13.0
gabbi===1.44.0
nwdiag===1.0.4
XStatic-bootswatch===3.3.7.0
XStatic-JS-Yaml===3.8.1.0
XStatic-term.js===0.0.7.0
oslo.log===3.39.0
nodeenv===1.3.2
pylev===1.3.0
python-searchlightclient===1.3.0
oslo.middleware===3.36.0
XStatic-mdi===1.4.57.0
django-pyscss===2.0.2
uritemplate===3.0.0
django-babel===0.6.2
docutils===0.14
notifier===1.0.3
ujson===1.35
selenium===3.14.0
python-glareclient===0.5.3
mypy===0.620;python_version=='3.4'
mypy===0.620;python_version=='3.5'
mypy===0.620;python_version=='3.6'
mistral-lib===1.0.0
dogtag-pki===10.3.5.1
XStatic-Angular-UUID===0.0.4.0
sphinxcontrib-seqdiag===0.8.5
os-win===4.0.1
dictdiffer===0.7.1
retrying===1.3.3
shade===1.29.0
pathlib2===2.3.2
pydotplus===2.0.2
flask-oslolog===0.1
jeepney===0.3.1;python_version=='3.4'
jeepney===0.3.1;python_version=='3.5'
jeepney===0.3.1;python_version=='3.6'
stestr===2.1.1
singledispatch===3.4.0.3;python_version=='2.7'
oslo.serialization===2.27.0
warlock===1.3.0
exabgp===4.0.8
sphinxcontrib-httpdomain===1.7.0
metalsmith===0.7.0
thriftpy===0.3.9;python_version=='2.7'
text-unidecode===1.2
murano-pkg-check===0.3.0
oslo.vmware===2.31.0
sqlalchemy-migrate===0.11.0
python-monascaclient===1.12.1
ldap3===2.5.1
requests-ntlm===1.1.0
python-string-utils===0.6.0
automaton===1.15.0
os-service-types===1.3.0
keyring===15.1.0
testscenarios===0.5.0
sphinxcontrib-pecanwsme===0.9.0
sadisplay===0.4.9
enum34===1.1.6
packaging===17.1
flask-keystone===0.2
nose-exclude===0.5.0
psutil===5.4.7
py===1.6.0
txaio===18.8.1
python-qinlingclient===2.0.0
elasticsearch===2.4.1
django-nose===1.4.5
XStatic-JQuery.TableSorter===2.14.5.1
pifpaf===2.1.1
pysmi===0.3.1
blockdiag===1.5.4
testtools===2.3.0
Parsley===1.3
XStatic-tv4===1.2.7.0
XStatic-JSEncrypt===2.3.1.1
python-cinderclient===4.0.1
keystonemiddleware===5.2.0
django-formtools===2.1
python-ceilometerclient===2.9.0
XStatic-Spin===1.2.5.2
openshift===0.7.1
tap-as-a-service===3.0.0
os-traits===0.9.0
SecretStorage===2.3.1;python_version=='2.7'
SecretStorage===3.1.0;python_version=='3.4'
SecretStorage===3.1.0;python_version=='3.5'
SecretStorage===3.1.0;python_version=='3.6'
opentracing===1.3.0
XStatic-Rickshaw===1.5.0.0
iso8601===0.1.12
tooz===1.62.0
linecache2===1.0.0
oauth2client===4.1.3
idna===2.7
python-karborclient===1.1.0
weakrefmethod===1.0.3;python_version=='2.7'
PuLP===1.6.8
crc16===0.1.1
protobuf===3.6.1
os-dpm===1.1.0
sushy===1.6.0
python-neutronclient===6.10.0
pika===0.12.0
oslo.cache===1.30.1
WebTest===2.0.30
openstack.nose-plugin===0.11
os-collect-config===9.2.0
python-qpid-proton===0.23.0
python-octaviaclient===1.6.0
pysaml2===4.6.2
requests-oauthlib===1.0.0
oslo.reports===1.28.0
ceilometermiddleware===1.3.0
python-nss===1.0.1
testrepository===0.0.20
sympy===1.3
sphinxmark===0.1.19
PyNaCl===1.2.1
osc-lib===1.11.1
python-consul===1.1.0
Faker===0.9.1
more-itertools===4.3.0
seqdiag===0.9.6
numpy===1.15.1
msgpack===0.5.6
Sphinx===1.8.0
oslo.config===6.4.0
tempest===19.0.0
django-floppyforms===1.7.0
openstackdocstheme===1.23.2
osc-placement===1.3.0
zake===0.2.2
python-rsdclient===0.1.3
python-magic===0.4.15
python-solumclient===2.7.1
PyMySQL===0.9.2
kubernetes===7.0.0
httplib2===0.11.3
bottle===0.12.13
betamax===0.8.1
construct===2.8.22
pyparsing===2.2.0
dogpile.cache===0.6.7
python-barbicanclient===4.7.0
tricircleclient===0.4.0
WSME===0.9.3
proboscis===1.2.6.0
fortiosclient===0.0.3
stevedore===1.29.0
botocore===1.12.4
xmltodict===0.11.0
pyasn1===0.4.4
oslo.rootwrap===5.14.1
Django===1.11.15;python_version=='2.7'
Django===2.0.8;python_version=='3.4'
Django===2.0.8;python_version=='3.5'
Django===2.0.8;python_version=='3.6'
pexpect===4.6.0
cmd2===0.8.9;python_version=='2.7'
cmd2===0.9.4;python_version=='3.4'
cmd2===0.9.4;python_version=='3.5'
cmd2===0.9.4;python_version=='3.6'
redis===2.10.6
jmespath===0.9.3
click===6.7
atomicwrites===1.2.1
docker-pycreds===0.3.0
XStatic-smart-table===1.4.13.2
kuryr-lib===0.8.0
scrypt===0.8.6
jsonpatch===1.23
python-daemon===2.2.0
typed-ast===1.1.0;python_version=='3.4'
typed-ast===1.1.0;python_version=='3.5'
typed-ast===1.1.0;python_version=='3.6'
os-testr===1.0.0
cotyledon===1.7.1
stomp.py===4.1.21
xattr===0.9.6
systemd-python===234
python-memcached===1.59
openstacksdk===0.17.2
six===1.11.0
dulwich===0.19.6
pykafka===2.7.0
kombu===4.2.1
distro===1.3.0
betamax-matchers===0.4.0
yaql===1.1.3
requestsexceptions===1.4.0
testresources===2.0.1
falcon===1.4.1
subprocess32===3.5.2;python_version=='2.7'
etcd3gw===0.2.4
Flask-RESTful===0.3.6
GitPython===2.1.11
python-ironicclient===2.5.0
XStatic===1.0.1
XStatic-Angular-FileUpload===12.0.4.0
python-openstackclient===3.16.1
pyzmq===17.1.2
oslo.db===4.40.0
simplegeneric===0.8.1
python-pcre===0.7
abclient===0.2.3
pymemcache===2.0.0
wrapt===1.10.11
oslo.privsep===1.29.0
sphinxcontrib-apidoc===0.2.1
oslo.policy===1.38.1
python-muranoclient===1.1.1
pyeclib===1.5.0
wsgi-intercept===1.8.0
ndg-httpsclient===0.5.1;python_version=='2.7'
repoze.lru===0.7
rfc3986===1.1.0
tenacity===5.0.2
python-designateclient===2.10.0
future===0.16.0
Paste===2.0.3
jaeger-client===3.11.0
XStatic-Json2yaml===0.1.1.0
boto===2.49.0
functools32===3.2.3.post2;python_version=='2.7'
os-vif===1.11.1
python-masakariclient===5.2.0
Werkzeug===0.14.1
pyasn1-modules===0.2.2
entrypoints===0.2.3
APScheduler===3.5.3
monotonic===1.5
python-smaugclient===0.0.8
python-troveclient===2.16.0
etcd3===0.8.1
XStatic-Bootstrap-Datepicker===1.3.1.0
CouchDB===1.2
netifaces===0.10.7
cachetools===2.1.0
ws4py===0.5.1
backports-abc===0.5;python_version=='2.7'
keystoneauth1===3.11.0
statsd===3.3.0
XenAPI===1.2
python-keystoneclient===3.17.0
ceilometer===11.0.0
demjson===2.2.4
diskimage-builder===2.17.0
heat-translator===1.1.0
python-magnumclient===2.10.0
docker===3.5.0
qpid-python===1.36.0.post1;python_version=='2.7'
contextlib2===0.5.5
XStatic-Angular-lrdragndrop===1.0.2.2
python-congressclient===1.11.0
ovsdbapp===0.12.1
aniso8601===3.0.2
rjsmin===1.0.12
icalendar===4.0.2
configparser===3.5.0;python_version=='2.7'
decorator===4.3.0
cffi===1.11.5
futurist===1.7.0
jsonschema===2.6.0
python-blazarclient===2.0.1
alembic===1.0.0
glance-store===0.26.1
sphinxcontrib-programoutput===0.11
sphinx-testing===0.7.2
dnspython===1.15.0
oauthlib===2.1.0
Babel===2.6.0
logutils===0.3.5
scandir===1.9.0;python_version=='2.7'
sphinxcontrib-fulltoc===1.2.0
smmap2===2.0.4
greenlet===0.4.15
XStatic-Angular-Vis===4.16.0.0
confluent-kafka===0.11.5
xvfbwrapper===0.2.9
futures===3.2.0;python_version=='2.7'
tosca-parser===1.1.0
Flask===1.0.2
happybase===1.1.0;python_version=='2.7'
marathon===0.10.0
fasteners===0.14.1
sortedcontainers===2.0.5
python-tackerclient===0.14.0
python-heatclient===1.16.1
kafka-python===1.4.3
oslo.utils===3.37.0
python-editor===1.0.3
gitdb2===2.0.4
requests-kerberos===0.12.0
itsdangerous===0.24
XStatic-jquery-ui===1.12.0.1
monasca-statsd===1.10.1
python-dateutil===2.7.3
virtualenv===16.0.0
colorama===0.3.9
ironic-lib===2.14.0
pytz===2018.5
XStatic-D3===3.5.17.0
actdiag===0.5.4
sysv-ipc===1.0.0
scikit-learn===0.19.2