318 lines
12 KiB
Python
318 lines
12 KiB
Python
# Copyright 2014 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 sys
|
|
import time
|
|
import traceback
|
|
|
|
from cinderclient.client import Client as CinderClient
|
|
from heatclient.v1.client import Client as HeatClient
|
|
from glanceclient import Client as GlanceClient
|
|
from ironicclient.client import get_client as get_ironic_client
|
|
from keystoneauth1.exceptions import ClientException
|
|
from keystoneauth1.identity import V2Password
|
|
from keystoneauth1.session import Session as KeystoneSession
|
|
from keystoneclient.v2_0 import Client as KeystoneClient
|
|
from novaclient.client import Client as NovaClient
|
|
from neutronclient.v2_0.client import Client as NeutronClient
|
|
from proboscis.asserts import assert_equal
|
|
import six
|
|
# pylint: disable=redefined-builtin
|
|
from six.moves import xrange
|
|
# pylint: enable=redefined-builtin
|
|
# pylint: disable=import-error
|
|
from six.moves import urllib
|
|
# pylint: enable=import-error
|
|
|
|
from fuelweb_test.helpers import checkers
|
|
from fuelweb_test.helpers.ssh_manager import SSHManager
|
|
from fuelweb_test import logger
|
|
from fuelweb_test import logwrap
|
|
from fuelweb_test.settings import DISABLE_SSL
|
|
from fuelweb_test.settings import PATH_TO_CERT
|
|
from fuelweb_test.settings import VERIFY_SSL
|
|
|
|
|
|
KeystoneSession._REDIRECT_STATUSES =\
|
|
KeystoneSession._REDIRECT_STATUSES + (308, )
|
|
logger.info("Temprary solution for keystone session class, bug: #1610914."
|
|
"Status code 308 was added to the tuple redirects")
|
|
|
|
|
|
class Common(object):
|
|
"""Common.""" # TODO documentation
|
|
|
|
def __make_endpoint(self, endpoint):
|
|
parse = urllib.parse.urlparse(endpoint)
|
|
return parse._replace(
|
|
netloc='{}:{}'.format(
|
|
self.controller_ip, parse.port)).geturl()
|
|
|
|
def __init__(self, controller_ip, user, password, tenant):
|
|
self.controller_ip = controller_ip
|
|
|
|
self.keystone_session = None
|
|
|
|
if DISABLE_SSL:
|
|
auth_url = 'http://{0}:5000/v2.0/'.format(self.controller_ip)
|
|
path_to_cert = None
|
|
else:
|
|
auth_url = 'https://{0}:5000/v2.0/'.format(self.controller_ip)
|
|
path_to_cert = PATH_TO_CERT
|
|
|
|
insecure = not VERIFY_SSL
|
|
|
|
logger.debug('Auth URL is {0}'.format(auth_url))
|
|
|
|
self.__keystone_auth = V2Password(
|
|
auth_url=auth_url,
|
|
username=user,
|
|
password=password,
|
|
tenant_name=tenant) # TODO: in v3 project_name
|
|
|
|
self.__start_keystone_session(ca_cert=path_to_cert, insecure=insecure)
|
|
|
|
@property
|
|
def keystone(self):
|
|
return KeystoneClient(session=self.keystone_session)
|
|
|
|
@property
|
|
def glance(self):
|
|
endpoint = self.__make_endpoint(
|
|
self._get_url_for_svc(service_type='image'))
|
|
return GlanceClient(
|
|
version='1',
|
|
session=self.keystone_session,
|
|
endpoint_override=endpoint)
|
|
|
|
@property
|
|
def neutron(self):
|
|
endpoint = self.__make_endpoint(
|
|
self._get_url_for_svc(service_type='network'))
|
|
return NeutronClient(
|
|
session=self.keystone_session,
|
|
endpoint_override=endpoint)
|
|
|
|
@property
|
|
def nova(self):
|
|
endpoint = self.__make_endpoint(
|
|
self._get_url_for_svc(service_type='compute'))
|
|
return NovaClient(
|
|
version='2',
|
|
session=self.keystone_session,
|
|
endpoint_override=endpoint)
|
|
|
|
@property
|
|
def cinder(self):
|
|
endpoint = self.__make_endpoint(
|
|
self._get_url_for_svc(service_type='volume'))
|
|
return CinderClient(
|
|
version='2',
|
|
session=self.keystone_session,
|
|
endpoint_override=endpoint)
|
|
|
|
@property
|
|
def heat(self):
|
|
endpoint = self.__make_endpoint(
|
|
self._get_url_for_svc(service_type='orchestration'))
|
|
return HeatClient(
|
|
session=self.keystone_session,
|
|
endpoint_override=endpoint)
|
|
|
|
@property
|
|
def ironic(self):
|
|
try:
|
|
endpoint = self.__make_endpoint(
|
|
self._get_url_for_svc(service_type='baremetal'))
|
|
return get_ironic_client('1', session=self.keystone_session,
|
|
insecure=True, ironic_url=endpoint)
|
|
except ClientException as e:
|
|
logger.warning('Could not initialize ironic client {0}'.format(e))
|
|
raise
|
|
|
|
@property
|
|
def keystone_access(self):
|
|
return self.__keystone_auth.get_access(session=self.keystone_session)
|
|
|
|
def _get_url_for_svc(
|
|
self, service_type=None, interface='public',
|
|
region_name=None, service_name=None,
|
|
service_id=None, endpoint_id=None
|
|
):
|
|
return self.keystone_access.service_catalog.url_for(
|
|
service_type=service_type, interface=interface,
|
|
region_name=region_name, service_name=service_name,
|
|
service_id=service_id, endpoint_id=endpoint_id
|
|
)
|
|
|
|
def goodbye_security(self):
|
|
secgroup_list = self.nova.security_groups.list()
|
|
logger.debug("Security list is {0}".format(secgroup_list))
|
|
secgroup_id = [i.id for i in secgroup_list if i.name == 'default'][0]
|
|
logger.debug("Id of security group default is {0}".format(
|
|
secgroup_id))
|
|
logger.debug('Permit all TCP and ICMP in security group default')
|
|
self.nova.security_group_rules.create(secgroup_id,
|
|
ip_protocol='tcp',
|
|
from_port=1,
|
|
to_port=65535)
|
|
self.nova.security_group_rules.create(secgroup_id,
|
|
ip_protocol='icmp',
|
|
from_port=-1,
|
|
to_port=-1)
|
|
|
|
def update_image(self, image, **kwargs):
|
|
self.glance.images.update(image.id, **kwargs)
|
|
return self.glance.images.get(image.id)
|
|
|
|
def delete_image(self, image_id):
|
|
return self.glance.images.delete(image_id)
|
|
|
|
def create_key(self, key_name):
|
|
logger.debug('Try to create key {0}'.format(key_name))
|
|
return self.nova.keypairs.create(key_name)
|
|
|
|
def create_instance(self, flavor_name='test_flavor', ram=64, vcpus=1,
|
|
disk=1, server_name='test_instance', image_name=None,
|
|
neutron_network=True, label=None):
|
|
logger.debug('Try to create instance')
|
|
|
|
start_time = time.time()
|
|
exc_type, exc_value, exc_traceback = None, None, None
|
|
while time.time() - start_time < 100:
|
|
try:
|
|
if image_name:
|
|
image = [i.id for i in self.nova.images.list()
|
|
if i.name == image_name]
|
|
else:
|
|
image = [i.id for i in self.nova.images.list()]
|
|
break
|
|
except Exception as e:
|
|
exc_type, exc_value, exc_traceback = sys.exc_info()
|
|
logger.warning('Ignoring exception: {!r}'.format(e))
|
|
logger.debug(traceback.format_exc())
|
|
else:
|
|
if all((exc_type, exc_traceback, exc_value)):
|
|
six.reraise(exc_type, exc_value, exc_traceback)
|
|
raise Exception('Can not get image')
|
|
|
|
kwargs = {}
|
|
if neutron_network:
|
|
net_label = label if label else 'net04'
|
|
network = self.nova.networks.find(label=net_label)
|
|
kwargs['nics'] = [{'net-id': network.id, 'v4-fixed-ip': ''}]
|
|
|
|
logger.info('image uuid is {0}'.format(image))
|
|
flavor = self.nova.flavors.create(
|
|
name=flavor_name, ram=ram, vcpus=vcpus, disk=disk)
|
|
logger.info('flavor is {0}'.format(flavor.name))
|
|
server = self.nova.servers.create(
|
|
name=server_name, image=image[0], flavor=flavor, **kwargs)
|
|
logger.info('server is {0}'.format(server.name))
|
|
return server
|
|
|
|
@logwrap
|
|
def get_instance_detail(self, server):
|
|
details = self.nova.servers.get(server)
|
|
return details
|
|
|
|
def verify_instance_status(self, server, expected_state):
|
|
def _verify_instance_state():
|
|
curr_state = self.get_instance_detail(server).status
|
|
assert_equal(expected_state, curr_state)
|
|
|
|
try:
|
|
_verify_instance_state()
|
|
except AssertionError:
|
|
logger.debug('Instance is not {0}, lets provide it the last '
|
|
'chance and sleep 60 sec'.format(expected_state))
|
|
time.sleep(60)
|
|
_verify_instance_state()
|
|
|
|
def delete_instance(self, server):
|
|
logger.debug('Try to delete instance')
|
|
self.nova.servers.delete(server)
|
|
|
|
def create_flavor(self, name, ram, vcpus, disk, flavorid="auto",
|
|
ephemeral=0, extra_specs=None):
|
|
flavor = self.nova.flavors.create(name, ram, vcpus, disk, flavorid,
|
|
ephemeral=ephemeral)
|
|
if extra_specs:
|
|
flavor.set_keys(extra_specs)
|
|
return flavor
|
|
|
|
def delete_flavor(self, flavor):
|
|
return self.nova.flavors.delete(flavor)
|
|
|
|
def create_aggregate(self, name, availability_zone=None,
|
|
metadata=None, hosts=None):
|
|
aggregate = self.nova.aggregates.create(
|
|
name=name, availability_zone=availability_zone)
|
|
for host in hosts or []:
|
|
aggregate.add_host(host)
|
|
if metadata:
|
|
aggregate.set_metadata(metadata)
|
|
return aggregate
|
|
|
|
def delete_aggregate(self, aggregate, hosts=None):
|
|
for host in hosts or []:
|
|
self.nova.aggregates.remove_host(aggregate, host)
|
|
return self.nova.aggregates.delete(aggregate)
|
|
|
|
def __start_keystone_session(
|
|
self, retries=3, ca_cert=None, insecure=not VERIFY_SSL):
|
|
exc_type, exc_value, exc_traceback = None, None, None
|
|
for i in xrange(retries):
|
|
try:
|
|
if insecure:
|
|
self.keystone_session = KeystoneSession(
|
|
auth=self.__keystone_auth, verify=False)
|
|
elif ca_cert:
|
|
self.keystone_session = KeystoneSession(
|
|
auth=self.__keystone_auth, verify=ca_cert)
|
|
else:
|
|
self.keystone_session = KeystoneSession(
|
|
auth=self.__keystone_auth)
|
|
self.keystone_session.get_auth_headers()
|
|
return
|
|
|
|
except ClientException as exc:
|
|
exc_type, exc_value, exc_traceback = sys.exc_info()
|
|
err = "Try nr {0}. Could not get keystone token, error: {1}"
|
|
logger.warning(err.format(i + 1, exc))
|
|
time.sleep(5)
|
|
if exc_type and exc_traceback and exc_value:
|
|
six.reraise(exc_type, exc_value, exc_traceback)
|
|
raise RuntimeError()
|
|
|
|
@staticmethod
|
|
def rebalance_swift_ring(controller_ip, retry_count=5, sleep=600):
|
|
"""Check Swift ring and rebalance it if needed.
|
|
|
|
Replication should be performed on primary controller node.
|
|
Retry check several times. Wait for replication due to LP1498368.
|
|
"""
|
|
ssh = SSHManager()
|
|
cmd = "/usr/local/bin/swift-rings-rebalance.sh"
|
|
logger.debug('Check swift ring and rebalance it.')
|
|
for _ in xrange(retry_count):
|
|
try:
|
|
checkers.check_swift_ring(controller_ip)
|
|
break
|
|
except AssertionError:
|
|
result = ssh.execute_on_remote(ip=controller_ip, cmd=cmd)
|
|
logger.debug("command execution result is {0}".format(result))
|
|
else:
|
|
checkers.check_swift_ring(controller_ip)
|