Implement image builder in Python
Change-Id: I7bd8b99b93b70e87a3780dfe535a425a6942f4b4
This commit is contained in:
@@ -24,12 +24,24 @@
|
||||
#os_password =
|
||||
|
||||
# Authentication region name, defaults to env[OS_REGION_NAME]. (string value)
|
||||
#os_region_name =
|
||||
#os_region_name = RegionOne
|
||||
|
||||
# Name or ID of external network. If not set the network is chosen randomly.
|
||||
# (string value)
|
||||
#external_net = <None>
|
||||
|
||||
# Name of image to use. The default is created by shaker-image-builder (string
|
||||
# value)
|
||||
#image_name = shaker-image
|
||||
|
||||
# Name of image flavor. The default is created by shaker-image-builder (string
|
||||
# value)
|
||||
#flavor_name = shaker-flavor
|
||||
|
||||
#
|
||||
# From shaker.engine.config
|
||||
#
|
||||
|
||||
# Scenario file name (string value)
|
||||
#scenario = <None>
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ oslo.config>=1.6.0 # Apache-2.0
|
||||
oslo.i18n>=1.3.0 # Apache-2.0
|
||||
oslo.serialization>=1.2.0 # Apache-2.0
|
||||
oslo.utils>=1.2.0 # Apache-2.0
|
||||
python-glanceclient>=0.15.0
|
||||
python-keystoneclient>=1.0.0
|
||||
python-neutronclient>=2.3.6,<3
|
||||
python-novaclient>=2.18.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[metadata]
|
||||
name = shaker
|
||||
summary = Shake VMs with our sheer-class tests!
|
||||
summary = Distributed data-plane performance testing tool
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
@@ -26,6 +26,8 @@ packages =
|
||||
console_scripts =
|
||||
shaker = shaker.engine.server:main
|
||||
shaker-agent = shaker.agent.agent:main
|
||||
shaker-image-builder = shaker.engine.installer:build_image
|
||||
shaker-cleanup = shaker.engine.installer:cleanup
|
||||
|
||||
oslo.config.opts =
|
||||
shaker.openstack.common.log = shaker.openstack.common.log:list_opts
|
||||
|
||||
@@ -25,7 +25,7 @@ COMMON_OPTS = [
|
||||
help='Address for server connections (host:port)'),
|
||||
]
|
||||
|
||||
SERVER_OPTS = [
|
||||
OPENSTACK_OPTS = [
|
||||
cfg.StrOpt('os-auth-url', metavar='<auth-url>',
|
||||
default=utils.env('OS_AUTH_URL'),
|
||||
help='Authentication URL, defaults to env[OS_AUTH_URL].'),
|
||||
@@ -40,7 +40,7 @@ SERVER_OPTS = [
|
||||
default=utils.env('OS_PASSWORD'),
|
||||
help='Authentication password, defaults to env[OS_PASSWORD].'),
|
||||
cfg.StrOpt('os-region-name', metavar='<auth-region-name>',
|
||||
default=utils.env('OS_REGION_NAME'),
|
||||
default=utils.env('OS_REGION_NAME') or 'RegionOne',
|
||||
help='Authentication region name, defaults to '
|
||||
'env[OS_REGION_NAME].'),
|
||||
|
||||
@@ -48,6 +48,17 @@ SERVER_OPTS = [
|
||||
help='Name or ID of external network. If not set the network '
|
||||
'is chosen randomly.'),
|
||||
|
||||
cfg.StrOpt('image-name',
|
||||
default='shaker-image',
|
||||
help='Name of image to use. The default is created by '
|
||||
'shaker-image-builder'),
|
||||
cfg.StrOpt('flavor-name',
|
||||
default='shaker-flavor',
|
||||
help='Name of image flavor. The default is created by '
|
||||
'shaker-image-builder'),
|
||||
]
|
||||
|
||||
SERVER_OPTS = [
|
||||
cfg.StrOpt('scenario',
|
||||
required=True,
|
||||
help='Scenario file name'),
|
||||
@@ -67,5 +78,6 @@ AGENT_OPTS = [
|
||||
|
||||
def list_opts():
|
||||
yield (None, copy.deepcopy(COMMON_OPTS))
|
||||
yield (None, copy.deepcopy(OPENSTACK_OPTS))
|
||||
yield (None, copy.deepcopy(SERVER_OPTS))
|
||||
yield (None, copy.deepcopy(AGENT_OPTS))
|
||||
|
||||
@@ -30,23 +30,23 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
class Deployment(object):
|
||||
def __init__(self, os_username, os_password, os_tenant_name, os_auth_url,
|
||||
os_region_name, server_endpoint, external_net):
|
||||
keystone_kwargs = {'username': os_username,
|
||||
'password': os_password,
|
||||
'tenant_name': os_tenant_name,
|
||||
'auth_url': os_auth_url,
|
||||
}
|
||||
self.keystone_client = keystone.create_keystone_client(keystone_kwargs)
|
||||
self.heat_client = heat.create_heat_client(
|
||||
os_region_name, server_endpoint, external_net, flavor_name,
|
||||
image_name):
|
||||
self.keystone_client = keystone.create_keystone_client(
|
||||
username=os_username, password=os_password,
|
||||
tenant_name=os_tenant_name, auth_url=os_auth_url)
|
||||
self.heat_client = heat.create_client(
|
||||
self.keystone_client, os_region_name)
|
||||
self.nova_client = nova.create_nova_client(
|
||||
self.nova_client = nova.create_client(
|
||||
self.keystone_client, os_region_name)
|
||||
self.neutron_client = neutron.create_neutron_client(
|
||||
self.neutron_client = neutron.create_client(
|
||||
self.keystone_client, os_region_name)
|
||||
|
||||
self.server_endpoint = server_endpoint
|
||||
self.external_net = (external_net or
|
||||
neutron.choose_external_net(self.neutron_client))
|
||||
self.flavor_name = flavor_name
|
||||
self.image_name = image_name
|
||||
|
||||
self.stack_name = 'shaker_%s' % uuid.uuid4()
|
||||
self.stack_deployed = False
|
||||
@@ -91,7 +91,7 @@ class Deployment(object):
|
||||
for param in params:
|
||||
o = stack_outputs.get(vm_name + '_' + param)
|
||||
if o:
|
||||
result[param] = o['output_value']
|
||||
result[param] = o
|
||||
return result
|
||||
|
||||
def convert_instance_name_to_agent_id(self, instance_name):
|
||||
@@ -133,36 +133,35 @@ class Deployment(object):
|
||||
|
||||
return agents
|
||||
|
||||
def _fill_missing_template_parameters(self, template_parameters):
|
||||
template_parameters['private_net_name'] = 'net_%s' % uuid.uuid4()
|
||||
template_parameters['server_endpoint'] = self.server_endpoint
|
||||
|
||||
if not template_parameters.get('external_net'):
|
||||
template_parameters['external_net'] = self.external_net
|
||||
|
||||
def _deploy_from_hot(self, specification):
|
||||
vm_accommodation = specification['vm_accommodation']
|
||||
heat_template_name = specification['template']
|
||||
template_parameters = specification['template_parameters']
|
||||
heat_template = utils.read_file(heat_template_name)
|
||||
groups = self._make_groups(vm_accommodation)
|
||||
groups = self._make_groups(specification['vm_accommodation'])
|
||||
|
||||
# render template by jinja
|
||||
vars_values = {
|
||||
'groups': groups,
|
||||
}
|
||||
heat_template = utils.read_file(specification['template'])
|
||||
compiled_template = jinja2.Template(heat_template)
|
||||
rendered_template = compiled_template.render(vars_values)
|
||||
LOG.debug('Rendered template: %s', rendered_template)
|
||||
|
||||
# create stack by Heat
|
||||
self._fill_missing_template_parameters(template_parameters)
|
||||
merged_parameters = {
|
||||
'private_net_name': 'net_%s' % uuid.uuid4(),
|
||||
'private_net_cidr': '10.0.0.0/16',
|
||||
'server_endpoint': self.server_endpoint,
|
||||
'external_net': self.external_net,
|
||||
'image': self.image_name,
|
||||
'flavor': self.flavor_name,
|
||||
}
|
||||
merged_parameters.update(specification['template_parameters'])
|
||||
|
||||
stack_params = {
|
||||
'stack_name': self.stack_name,
|
||||
'parameters': template_parameters,
|
||||
'parameters': merged_parameters,
|
||||
'template': rendered_template,
|
||||
}
|
||||
LOG.debug('Creating stack with parameters: %s', stack_params)
|
||||
|
||||
stack = self.heat_client.stacks.create(**stack_params)['stack']
|
||||
LOG.info('New stack: %s', stack)
|
||||
@@ -171,9 +170,7 @@ class Deployment(object):
|
||||
self.stack_deployed = True
|
||||
|
||||
# get info about deployed objects
|
||||
outputs_list = self.heat_client.stacks.get(
|
||||
stack['id']).to_dict()['outputs']
|
||||
outputs = dict((item['output_key'], item) for item in outputs_list)
|
||||
outputs = heat.get_stack_outputs(self.heat_client, stack['id'])
|
||||
|
||||
# convert groups into agents
|
||||
return self._make_agents(groups, outputs)
|
||||
|
||||
113
shaker/engine/installer.py
Normal file
113
shaker/engine/installer.py
Normal file
@@ -0,0 +1,113 @@
|
||||
# Copyright (c) 2015 Mirantis Inc.
|
||||
#
|
||||
# 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 uuid
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from shaker.engine import config
|
||||
from shaker.engine import utils
|
||||
from shaker.openstack.clients import glance
|
||||
from shaker.openstack.clients import heat
|
||||
from shaker.openstack.clients import neutron
|
||||
from shaker.openstack.clients import nova
|
||||
from shaker.openstack.clients import openstack
|
||||
from shaker.openstack.common import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def init():
|
||||
# init conf and logging
|
||||
conf = cfg.CONF
|
||||
conf.register_cli_opts(config.OPENSTACK_OPTS)
|
||||
conf.register_opts(config.OPENSTACK_OPTS)
|
||||
conf(project='shaker')
|
||||
|
||||
logging.setup('shaker')
|
||||
LOG.info('Logging enabled')
|
||||
|
||||
openstack_client = openstack.OpenStackClient(
|
||||
username=cfg.CONF.os_username, password=cfg.CONF.os_password,
|
||||
tenant_name=cfg.CONF.os_tenant_name, auth_url=cfg.CONF.os_auth_url,
|
||||
region_name=cfg.CONF.os_region_name)
|
||||
return openstack_client
|
||||
|
||||
|
||||
def build_image():
|
||||
openstack_client = init()
|
||||
flavor_name = cfg.CONF.flavor_name
|
||||
image_name = cfg.CONF.image_name
|
||||
|
||||
if nova.is_flavor_exists(openstack_client.nova, flavor_name):
|
||||
LOG.info('Using existing flavor: %s', flavor_name)
|
||||
else:
|
||||
openstack_client.nova.flavors.create(name=flavor_name,
|
||||
ram=1024, vcpus=1, disk=3)
|
||||
LOG.info('Created flavor %s', flavor_name)
|
||||
|
||||
if glance.get_image(openstack_client.glance, image_name):
|
||||
LOG.info('Using existing image: %s', image_name)
|
||||
else:
|
||||
external_net = (cfg.CONF.external_net or
|
||||
neutron.choose_external_net(openstack_client.neutron))
|
||||
stack_params = {
|
||||
'stack_name': 'shaker_%s' % uuid.uuid4(),
|
||||
'parameters': {'external_net': external_net,
|
||||
'flavor': flavor_name},
|
||||
'template': utils.read_file('shaker/engine/installer.yaml'),
|
||||
}
|
||||
|
||||
stack = openstack_client.heat.stacks.create(**stack_params)['stack']
|
||||
LOG.debug('New stack: %s', stack)
|
||||
|
||||
heat.wait_stack_completion(openstack_client.heat, stack['id'])
|
||||
|
||||
outputs = heat.get_stack_outputs(openstack_client.heat, stack['id'])
|
||||
LOG.debug('Stack outputs: %s', outputs)
|
||||
|
||||
LOG.debug('Waiting for server to shutdown')
|
||||
server_id = outputs['server_info'].get('id')
|
||||
nova.wait_server_shutdown(openstack_client.nova, server_id)
|
||||
|
||||
LOG.debug('Making snapshot')
|
||||
openstack_client.nova.servers.create_image(
|
||||
server_id, image_name)
|
||||
|
||||
LOG.debug('Waiting for server to snapshot')
|
||||
nova.wait_server_snapshot(openstack_client.nova, server_id)
|
||||
|
||||
LOG.debug('Clearing up')
|
||||
openstack_client.heat.stacks.delete(stack['id'])
|
||||
|
||||
LOG.info('Created image: %s', image_name)
|
||||
|
||||
|
||||
def cleanup():
|
||||
openstack_client = init()
|
||||
flavor_name = cfg.CONF.flavor_name
|
||||
image_name = cfg.CONF.image_name
|
||||
|
||||
image = glance.get_image(openstack_client.glance, image_name)
|
||||
if image:
|
||||
openstack_client.glance.images.delete(image)
|
||||
|
||||
if nova.is_flavor_exists(openstack_client.nova, flavor_name):
|
||||
openstack_client.nova.flavors.delete(name=flavor_name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
build_image()
|
||||
82
shaker/engine/installer.yaml
Normal file
82
shaker/engine/installer.yaml
Normal file
@@ -0,0 +1,82 @@
|
||||
heat_template_version: 2013-05-23
|
||||
|
||||
description: >
|
||||
HOT template to create a new neutron network plus a router to the public
|
||||
network, and for deploying servers into the new network.
|
||||
|
||||
parameters:
|
||||
external_net:
|
||||
type: string
|
||||
description: ID or name of public network for which floating IP addresses will be allocated
|
||||
flavor:
|
||||
type: string
|
||||
description: Flavor to use for servers
|
||||
|
||||
resources:
|
||||
private_net:
|
||||
type: OS::Neutron::Net
|
||||
properties:
|
||||
name: shaker_image_builder_net
|
||||
|
||||
private_subnet:
|
||||
type: OS::Neutron::Subnet
|
||||
properties:
|
||||
network_id: { get_resource: private_net }
|
||||
cidr: 10.0.0.0/29
|
||||
dns_nameservers: [ 8.8.8.8, 8.8.4.4 ]
|
||||
|
||||
router:
|
||||
type: OS::Neutron::Router
|
||||
properties:
|
||||
external_gateway_info:
|
||||
network: { get_param: external_net }
|
||||
|
||||
router_interface:
|
||||
type: OS::Neutron::RouterInterface
|
||||
properties:
|
||||
router_id: { get_resource: router }
|
||||
subnet_id: { get_resource: private_subnet }
|
||||
|
||||
master_image:
|
||||
type: OS::Glance::Image
|
||||
properties:
|
||||
container_format: bare
|
||||
disk_format: qcow2
|
||||
location: https://cloud-images.ubuntu.com/releases/14.04.1/release/ubuntu-14.04-server-cloudimg-amd64-disk1.img
|
||||
min_disk: 3
|
||||
min_ram: 1000
|
||||
name: shaker_image_build_template
|
||||
|
||||
master_image_server_port:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network_id: { get_resource: private_net }
|
||||
fixed_ips:
|
||||
- subnet_id: { get_resource: private_subnet }
|
||||
|
||||
master_image_server:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
name: shaker_image_builder_server
|
||||
image: { get_resource: master_image }
|
||||
flavor: { get_param: flavor }
|
||||
networks:
|
||||
- port: { get_resource: master_image_server_port }
|
||||
user_data_format: RAW
|
||||
user_data:
|
||||
str_replace:
|
||||
template: |
|
||||
#!/bin/sh
|
||||
sudo apt-add-repository "deb http://nova.clouds.archive.ubuntu.com/ubuntu/ trusty multiverse"
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install iperf netperf git python-dev libzmq-dev screen
|
||||
wget -O get-pip.py https://bootstrap.pypa.io/get-pip.py && sudo python get-pip.py
|
||||
sudo pip install pbr netperf-wrapper
|
||||
git clone git://git.openstack.org/stackforge/shaker && cd shaker && sudo pip install -r requirements.txt && sudo python setup.py develop
|
||||
sudo shutdown -P -f now
|
||||
params:
|
||||
"$UNUSED": foo
|
||||
|
||||
outputs:
|
||||
server_info:
|
||||
value: { get_attr: [ master_image_server, show ] }
|
||||
@@ -192,8 +192,10 @@ def main():
|
||||
# init conf and logging
|
||||
conf = cfg.CONF
|
||||
conf.register_cli_opts(config.COMMON_OPTS)
|
||||
conf.register_cli_opts(config.OPENSTACK_OPTS)
|
||||
conf.register_cli_opts(config.SERVER_OPTS)
|
||||
conf.register_opts(config.COMMON_OPTS)
|
||||
conf.register_opts(config.OPENSTACK_OPTS)
|
||||
conf.register_opts(config.SERVER_OPTS)
|
||||
|
||||
try:
|
||||
@@ -211,9 +213,11 @@ def main():
|
||||
cfg.CONF.os_password,
|
||||
cfg.CONF.os_tenant_name,
|
||||
cfg.CONF.os_auth_url,
|
||||
cfg.CONF.os_region_name or 'RegionOne',
|
||||
cfg.CONF.os_region_name,
|
||||
cfg.CONF.server_endpoint,
|
||||
cfg.CONF.external_net)
|
||||
cfg.CONF.external_net,
|
||||
cfg.CONF.flavor_name,
|
||||
cfg.CONF.image_name)
|
||||
agents = deployment.deploy(scenario['deployment'])
|
||||
|
||||
if not agents:
|
||||
|
||||
34
shaker/openstack/clients/glance.py
Normal file
34
shaker/openstack/clients/glance.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# Copyright (c) 2015 Mirantis Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from glanceclient import client as glance_client_pkg
|
||||
|
||||
|
||||
GLANCE_VERSION = '1'
|
||||
|
||||
|
||||
def create_client(keystone_client, os_region_name):
|
||||
image_api_url = keystone_client.service_catalog.url_for(
|
||||
service_type='image', region_name=os_region_name)
|
||||
return glance_client_pkg.Client(GLANCE_VERSION,
|
||||
endpoint=image_api_url,
|
||||
token=keystone_client.auth_token)
|
||||
|
||||
|
||||
def get_image(glance_client, image_name):
|
||||
for image in glance_client.images.list():
|
||||
if image.name == image_name:
|
||||
return image
|
||||
return None
|
||||
@@ -26,13 +26,12 @@ LOG = logging.getLogger(__name__)
|
||||
HEAT_VERSION = '1'
|
||||
|
||||
|
||||
def create_heat_client(keystone_client, os_region_name):
|
||||
def create_client(keystone_client, os_region_name):
|
||||
orchestration_api_url = keystone_client.service_catalog.url_for(
|
||||
service_type='orchestration', region_name=os_region_name)
|
||||
client = heat_client_pkg.Client(HEAT_VERSION,
|
||||
endpoint=orchestration_api_url,
|
||||
token=keystone_client.auth_token, )
|
||||
return client
|
||||
return heat_client_pkg.Client(HEAT_VERSION,
|
||||
endpoint=orchestration_api_url,
|
||||
token=keystone_client.auth_token)
|
||||
|
||||
|
||||
def wait_stack_completion(heat_client, stack_id):
|
||||
@@ -48,3 +47,9 @@ def wait_stack_completion(heat_client, stack_id):
|
||||
|
||||
if status != 'COMPLETE':
|
||||
raise Exception(status)
|
||||
|
||||
|
||||
def get_stack_outputs(heat_client, stack_id):
|
||||
outputs_list = heat_client.stacks.get(stack_id).to_dict()['outputs']
|
||||
return dict((item['output_key'], item['output_value'])
|
||||
for item in outputs_list)
|
||||
|
||||
@@ -18,13 +18,13 @@ from keystoneclient.v2_0 import client as keystone_v2
|
||||
from keystoneclient.v3 import client as keystone_v3
|
||||
|
||||
|
||||
def create_keystone_client(args):
|
||||
discover = keystone_discover.Discover(**args)
|
||||
def create_keystone_client(**kwargs):
|
||||
discover = keystone_discover.Discover(**kwargs)
|
||||
for version_data in discover.version_data():
|
||||
version = version_data["version"]
|
||||
if version[0] <= 2:
|
||||
return keystone_v2.Client(**args)
|
||||
return keystone_v2.Client(**kwargs)
|
||||
elif version[0] == 3:
|
||||
return keystone_v3.Client(**args)
|
||||
return keystone_v3.Client(**kwargs)
|
||||
raise Exception(
|
||||
'Failed to discover keystone version for url %(auth_url)s.', **args)
|
||||
'Failed to discover keystone version for url %(auth_url)s.', **kwargs)
|
||||
|
||||
@@ -24,13 +24,12 @@ LOG = logging.getLogger(__name__)
|
||||
NEUTRON_VERSION = '2.0'
|
||||
|
||||
|
||||
def create_neutron_client(keystone_client, os_region_name):
|
||||
def create_client(keystone_client, os_region_name):
|
||||
network_api_url = keystone_client.service_catalog.url_for(
|
||||
service_type='network', region_name=os_region_name)
|
||||
client = neutron_client_pkg.Client(NEUTRON_VERSION,
|
||||
endpoint_url=network_api_url,
|
||||
token=keystone_client.auth_token, )
|
||||
return client
|
||||
return neutron_client_pkg.Client(NEUTRON_VERSION,
|
||||
endpoint_url=network_api_url,
|
||||
token=keystone_client.auth_token)
|
||||
|
||||
|
||||
def choose_external_net(neutron_client):
|
||||
|
||||
@@ -13,13 +13,19 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import time
|
||||
|
||||
from novaclient import client as nova_client_pkg
|
||||
|
||||
from shaker.openstack.common import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
NOVA_VERSION = '2'
|
||||
|
||||
|
||||
def create_nova_client(keystone_client, os_region_name):
|
||||
def create_client(keystone_client, os_region_name):
|
||||
compute_api_url = keystone_client.service_catalog.url_for(
|
||||
service_type='compute', region_name=os_region_name)
|
||||
client = nova_client_pkg.Client(NOVA_VERSION,
|
||||
@@ -30,3 +36,43 @@ def create_nova_client(keystone_client, os_region_name):
|
||||
|
||||
def get_compute_nodes(nova_client):
|
||||
return nova_client.services.list(binary='nova-compute')
|
||||
|
||||
|
||||
def is_flavor_exists(nova_client, flavor_name):
|
||||
for flavor in nova_client.flavors.list():
|
||||
if flavor.to_dict()['name'] == flavor_name:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _poll_for_status(poll_fn, obj_id, final_ok_states, poll_period=20,
|
||||
status_field="status"):
|
||||
LOG.debug('Poll server %(id)s, waiting for any of statuses %(statuses)s',
|
||||
dict(id=obj_id, statuses=final_ok_states))
|
||||
while True:
|
||||
obj = poll_fn(obj_id)
|
||||
status = getattr(obj, status_field)
|
||||
if status:
|
||||
status = status.lower()
|
||||
|
||||
LOG.debug('Server %(id)s has status %(status)s',
|
||||
dict(id=obj_id, status=status))
|
||||
|
||||
if status in final_ok_states:
|
||||
break
|
||||
elif status == "error":
|
||||
raise Exception(obj.fault['message'])
|
||||
|
||||
time.sleep(poll_period)
|
||||
|
||||
|
||||
def wait_server_shutdown(nova_client, server_id):
|
||||
_poll_for_status(nova_client.servers.get, server_id, ['shutoff'])
|
||||
|
||||
|
||||
def wait_server_snapshot(nova_client, server_id):
|
||||
task_state_field = "OS-EXT-STS:task_state"
|
||||
server = nova_client.servers.get(server_id)
|
||||
if hasattr(server, task_state_field):
|
||||
_poll_for_status(nova_client.servers.get, server.id, [None, '-', ''],
|
||||
status_field=task_state_field)
|
||||
|
||||
44
shaker/openstack/clients/openstack.py
Normal file
44
shaker/openstack/clients/openstack.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# Copyright (c) 2015 Mirantis Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from shaker.openstack.clients import glance
|
||||
from shaker.openstack.clients import heat
|
||||
from shaker.openstack.clients import keystone
|
||||
from shaker.openstack.clients import neutron
|
||||
from shaker.openstack.clients import nova
|
||||
|
||||
|
||||
CLIENT_MAKERS = {
|
||||
'glance': glance.create_client,
|
||||
'heat': heat.create_client,
|
||||
'neutron': neutron.create_client,
|
||||
'nova': nova.create_client,
|
||||
}
|
||||
|
||||
|
||||
class OpenStackClient(object):
|
||||
def __init__(self, username, password, tenant_name, auth_url, region_name):
|
||||
super(OpenStackClient, self).__init__()
|
||||
|
||||
self.keystone_client = keystone.create_keystone_client(
|
||||
username=username, password=password, tenant_name=tenant_name,
|
||||
auth_url=auth_url)
|
||||
self.region_name = region_name or 'RegionOne'
|
||||
|
||||
def __getattribute__(self, name):
|
||||
if name in CLIENT_MAKERS:
|
||||
return CLIENT_MAKERS[name](self.keystone_client, self.region_name)
|
||||
else:
|
||||
return super(OpenStackClient, self).__getattribute__(name)
|
||||
Reference in New Issue
Block a user