Use a single keystone instance for all the clients
This commit allows flame to be used with either a login and a password or an OS_AUTH_TOKEN. Change-Id: Ie34f2a4e2e0eb82967c7617bd0ecc1aebd934cb7
This commit is contained in:
parent
c9ee415228
commit
168d10bb1c
11
README.rst
11
README.rst
|
@ -13,8 +13,9 @@ for Nova (key pairs and servers), Cinder (volumes) and Neutron (router,
|
|||
networks, subnets, security groups and floating IPs) resources.
|
||||
|
||||
`flame` works as follows: using provided credentials (user name, project name,
|
||||
password, authentication url), the tool will list supported resources deployed
|
||||
in the project and will generate corresponding, highly customized HOT template.
|
||||
password or auth_token, authentication url), the tool will list supported
|
||||
resources deployed in the project and will generate corresponding, highly
|
||||
customized HOT template.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
@ -46,6 +47,8 @@ Usage
|
|||
--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].
|
||||
--insecure Explicitly allow clients to perform"insecure" SSL
|
||||
(https) requests. The server's certificate will not be
|
||||
verified against any certificate authorities. This
|
||||
|
@ -67,3 +70,7 @@ To use Flame you can provide yours OpenStack credentials as arguments :
|
|||
|
||||
|
||||
Or you can source your OpenStack RC file and use Flame without arguments.
|
||||
|
||||
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).
|
||||
|
|
|
@ -9,8 +9,9 @@ To use install flame in a project::
|
|||
To use the CLI of flame::
|
||||
|
||||
usage: flame [-h] [--username USERNAME] [--password PASSWORD]
|
||||
[--project PROJECT] [--auth_url AUTH_URL] [--insecure]
|
||||
[--exclude_servers] [--exclude_volumes]
|
||||
[--project PROJECT] [--os-auth-token OS_AUTH_TOKEN]
|
||||
[--auth_url AUTH_URL] [--insecure] [--exclude_servers]
|
||||
[--exclude_volumes]
|
||||
|
||||
Generate Heat Template
|
||||
|
||||
|
@ -21,6 +22,8 @@ To use the CLI of flame::
|
|||
--password PASSWORD The user's password. Defaults to env[OS_PASSWORD]
|
||||
--project PROJECT Name of project. Defaults to env[OS_TENANT_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].
|
||||
--insecure Explicitly allow clients to perform"insecure" SSL
|
||||
(https) requests. The server's certificate will not be
|
||||
verified against any certificate authorities. This
|
||||
|
@ -30,7 +33,7 @@ To use the CLI of flame::
|
|||
--generate-stack-data
|
||||
In addition to template, generate Heat stack data
|
||||
file.
|
||||
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
@ -40,6 +43,11 @@ 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/
|
||||
|
||||
Or a token and a tenant::
|
||||
|
||||
$ flame --username arezmerita --os-auth-token keystonetoken \
|
||||
--project project-arezmerita --auth_url https://example.com/v2.0/
|
||||
|
||||
Or you can source your OpenStack RC file and use Flame without arguments::
|
||||
|
||||
$ source credential.rc
|
||||
|
|
|
@ -26,12 +26,24 @@ from flameclient.flame import TemplateGenerator # noqa
|
|||
|
||||
|
||||
class Client(object):
|
||||
def __init__(self, username, password, tenant_name, auth_url, **kwargs):
|
||||
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, include_networks, include_instances, include_volumes):
|
||||
return self.template_generator.generate(include_networks,
|
||||
include_instances,
|
||||
include_volumes)
|
||||
def generate(self, exclude_servers, exclude_volumes, exclude_keypairs,
|
||||
generate_stack_data):
|
||||
self.template_generator.extract_vm_details(exclude_servers,
|
||||
exclude_volumes,
|
||||
exclude_keypairs,
|
||||
generate_stack_data
|
||||
)
|
||||
self.template_generator.extract_data()
|
||||
heat_template = self.template_generator.heat_template()
|
||||
if generate_stack_data:
|
||||
stack_data = self.template_generator.stack_data_template()
|
||||
else:
|
||||
stack_data = None
|
||||
return (heat_template, stack_data)
|
||||
|
|
|
@ -53,6 +53,10 @@ def main(args=None):
|
|||
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 "
|
||||
|
@ -80,9 +84,10 @@ def main(args=None):
|
|||
args = parser.parse_args()
|
||||
flame = client.Client(args.username, args.password,
|
||||
args.project, args.auth_url,
|
||||
insecure=args.insecure,
|
||||
args.os_auth_token,
|
||||
region_name=args.region,
|
||||
endpoint_type=args.endpoint_type,
|
||||
region_name=args.region)
|
||||
insecure=args.insecure)
|
||||
template = flame.template_generator
|
||||
template.extract_vm_details(args.exclude_servers,
|
||||
args.exclude_volumes,
|
||||
|
|
|
@ -105,11 +105,12 @@ class Resource(object):
|
|||
class TemplateGenerator(object):
|
||||
|
||||
def __init__(self, username, password, tenant_name, auth_url,
|
||||
insecure=False, endpoint_type='publicURL', region_name=None):
|
||||
auth_token=None, insecure=False, endpoint_type='publicURL',
|
||||
region_name=None):
|
||||
self.generate_data = False
|
||||
self._setup_templates()
|
||||
self._setup_managers(username, password, tenant_name, auth_url,
|
||||
insecure, endpoint_type, region_name=region_name)
|
||||
insecure, endpoint_type, region_name, auth_token)
|
||||
|
||||
def _setup_templates(self):
|
||||
self.template = yaml.load(template_skeleton)
|
||||
|
@ -120,19 +121,20 @@ class TemplateGenerator(object):
|
|||
self.stack_data['resources'] = {}
|
||||
|
||||
def _setup_managers(self, username, password, tenant_name, auth_url,
|
||||
insecure, endpoint_type, region_name=None):
|
||||
self.neutron = managers.NeutronManager(username, password, tenant_name,
|
||||
auth_url, insecure,
|
||||
endpoint_type,
|
||||
region_name=region_name)
|
||||
self.nova = managers.NovaManager(username, password, tenant_name,
|
||||
auth_url, insecure,
|
||||
endpoint_type,
|
||||
region_name=region_name)
|
||||
self.cinder = managers.CinderManager(username, password, tenant_name,
|
||||
auth_url, insecure,
|
||||
endpoint_type,
|
||||
region_name=region_name)
|
||||
insecure, endpoint_type, region_name=None,
|
||||
auth_token=None):
|
||||
self.keystone = managers.KeystoneManager(
|
||||
username, password,
|
||||
tenant_name,
|
||||
auth_url, insecure,
|
||||
endpoint_type,
|
||||
region_name=region_name,
|
||||
auth_token=auth_token
|
||||
)
|
||||
self.keystone.authenticate()
|
||||
self.neutron = managers.NeutronManager(self.keystone)
|
||||
self.nova = managers.NovaManager(self.keystone)
|
||||
self.cinder = managers.CinderManager(self.keystone)
|
||||
|
||||
def extract_vm_details(self, exclude_servers, exclude_volumes,
|
||||
exclude_keypairs, generate_data):
|
||||
|
@ -523,10 +525,13 @@ class TemplateGenerator(object):
|
|||
"Snapshot to create volume %s from" % resource_name)
|
||||
resource.add_parameter(key, description,
|
||||
default=volume.snapshot_id)
|
||||
if volume.display_name:
|
||||
properties['name'] = volume.display_name
|
||||
if volume.display_description:
|
||||
properties['description'] = volume.display_description
|
||||
try:
|
||||
if volume.display_name:
|
||||
properties['name'] = volume.display_name
|
||||
if volume.display_description:
|
||||
properties['description'] = volume.display_description
|
||||
except AttributeError:
|
||||
pass
|
||||
if volume.volume_type and volume.volume_type != 'None':
|
||||
key = "%s_volume_type" % resource_name
|
||||
description = (
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
# SOFTWARE.
|
||||
|
||||
from cinderclient.v1 import client as cinder_client
|
||||
from keystoneclient import exceptions as keystone_exceptions
|
||||
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
|
||||
|
@ -34,7 +33,7 @@ class KeystoneManager(object):
|
|||
_client = None
|
||||
|
||||
def __init__(self, username, password, project, auth_url, insecure,
|
||||
endpoint_type='publicURL', region_name=None):
|
||||
endpoint_type='publicURL', region_name=None, auth_token=None):
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.project = project
|
||||
|
@ -42,6 +41,11 @@ class KeystoneManager(object):
|
|||
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:
|
||||
|
@ -52,7 +56,8 @@ class KeystoneManager(object):
|
|||
auth_url=self.auth_url,
|
||||
region_name=self.region_name,
|
||||
insecure=self.insecure,
|
||||
endpoint_type=self.endpoint_type)
|
||||
endpoint_type=self.endpoint_type,
|
||||
token=self.auth_token)
|
||||
return self._client
|
||||
|
||||
def set_client(self, client):
|
||||
|
@ -73,32 +78,19 @@ class NeutronManager(object):
|
|||
_client = None
|
||||
_project_id = None
|
||||
|
||||
def __init__(self, username, password, project, auth_url, insecure,
|
||||
endpoint_type='publicURL', region_name=None):
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.project = project
|
||||
self.auth_url = auth_url
|
||||
self.insecure = insecure
|
||||
self.endpoint_type = endpoint_type
|
||||
self.region_name = region_name
|
||||
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(
|
||||
username=self.username,
|
||||
password=self.password,
|
||||
tenant_name=self.project,
|
||||
auth_url=self.auth_url,
|
||||
region_name=self.region_name,
|
||||
insecure=self.insecure,
|
||||
endpoint_type=self.endpoint_type)
|
||||
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:
|
||||
keystone_mgr = KeystoneManager(self.username, self.password,
|
||||
self.project, self.auth_url,
|
||||
self.insecure,
|
||||
region_name=self.region_name)
|
||||
self._project_id = keystone_mgr.get_project_id()
|
||||
self._project_id = self.keystone_mgr.get_project_id()
|
||||
return self._client
|
||||
|
||||
def set_client(self, client):
|
||||
|
@ -142,24 +134,22 @@ class NovaManager(object):
|
|||
"""Manage nova resources."""
|
||||
_client = None
|
||||
|
||||
def __init__(self, username, password, project, auth_url, insecure,
|
||||
endpoint_type='publicURL', region_name=None):
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.project = project
|
||||
self.auth_url = auth_url
|
||||
self.region_name = region_name
|
||||
self.insecure = insecure
|
||||
self.endpoint_type = endpoint_type
|
||||
def __init__(self, keystone_mgr):
|
||||
self.keystone_mgr = keystone_mgr
|
||||
|
||||
def client(self):
|
||||
if not self._client:
|
||||
self._client = nova_client.Client('2',
|
||||
self.username, self.password,
|
||||
self.project, self.auth_url,
|
||||
region_name=self.region_name,
|
||||
insecure=self.insecure,
|
||||
endpoint_type=self.endpoint_type)
|
||||
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):
|
||||
|
@ -191,35 +181,27 @@ class CinderManager(object):
|
|||
"""Manage Cinder resources."""
|
||||
_client = None
|
||||
|
||||
def __init__(self, username, password, project, auth_url, insecure,
|
||||
endpoint_type='publicURL', region_name=None):
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.project = project
|
||||
self.auth_url = auth_url
|
||||
self.region_name = region_name
|
||||
self.insecure = insecure
|
||||
def __init__(self, keystone_mgr):
|
||||
self.keystone_mgr = keystone_mgr
|
||||
self.defined = True
|
||||
self.endpoint_type = endpoint_type
|
||||
|
||||
def client(self):
|
||||
if self.defined and not self._client:
|
||||
client = cinder_client.Client(self.username,
|
||||
self.password,
|
||||
self.project,
|
||||
self.auth_url,
|
||||
region_name=self.region_name,
|
||||
insecure=self.insecure,
|
||||
endpoint_type=self.endpoint_type)
|
||||
|
||||
# Check cinder endpoint existence
|
||||
try:
|
||||
client.authenticate()
|
||||
self._client = client
|
||||
except keystone_exceptions.EndpointNotFound:
|
||||
self.defined = False
|
||||
self._client = None
|
||||
|
||||
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):
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
import mock
|
||||
|
||||
from flameclient import client as flame_client
|
||||
from flameclient import flame
|
||||
from flameclient.tests import base
|
||||
|
||||
|
@ -238,11 +239,16 @@ class BaseTestCase(base.TestCase):
|
|||
self.mock_nova = self.patch_nova.start()
|
||||
self.patch_cinder = mock.patch('flameclient.managers.CinderManager')
|
||||
self.mock_cinder = self.patch_cinder.start()
|
||||
self.patch_keystone = mock.patch(
|
||||
'flameclient.managers.KeystoneManager'
|
||||
)
|
||||
self.mock_keystone = self.patch_keystone.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.mock_neutron.stop()
|
||||
self.mock_nova.stop()
|
||||
self.mock_cinder.stop()
|
||||
self.mock_keystone.stop()
|
||||
super(BaseTestCase, self).tearDown()
|
||||
|
||||
def get_generator(self, exclude_servers, exclude_volumes,
|
||||
|
@ -274,6 +280,32 @@ class BaseTestCase(base.TestCase):
|
|||
self.assertEqual(expected_parameters, merged_parameters)
|
||||
|
||||
|
||||
class ClientTest(BaseTestCase):
|
||||
def setUp(self):
|
||||
super(ClientTest, self).setUp()
|
||||
self.c = flame_client.Client('username', 'password', 'tenant_name',
|
||||
'authUrl', 'auth_token')
|
||||
|
||||
def test_generate(self):
|
||||
out = self.c.generate(False, False, False, True)
|
||||
self.assertIsInstance(out, tuple)
|
||||
self.assertIsNotNone(out[1])
|
||||
|
||||
def test_generate_no_stack_data(self):
|
||||
out = self.c.generate(False, False, False, False)
|
||||
self.assertIsInstance(out, tuple)
|
||||
self.assertIsNotNone(out[0])
|
||||
self.assertIsNone(out[1])
|
||||
|
||||
def test_generate_contains_extract(self):
|
||||
generator = self.get_generator(False, False, False, True)
|
||||
out = self.c.generate(False, False, False, True)
|
||||
generator.extract_data()
|
||||
stack_data = generator.stack_data_template()
|
||||
heat_template = generator.heat_template()
|
||||
self.assertEqual(out, (heat_template, stack_data))
|
||||
|
||||
|
||||
class StackDataTests(BaseTestCase):
|
||||
|
||||
def test_keypair(self):
|
||||
|
|
Loading…
Reference in New Issue