Quique Llorente a2568a5598 Adapt python-tempestconf to python3
As part of running tripleo standalone jobs at fedora28 we found that
parts of tempestconf were not adapted to python3, this review just add
needed stuff to run tripleo-fedora-28-standalone job there.

Story: https://tree.taiga.io/project/tripleo-ci-board/task/478?kanban-status=1447276

Change-Id: I850ff56c4ced2f3cbab05bb8c3b120e112a2acdd
2018-12-12 12:35:26 +00:00

224 lines
8.4 KiB
Python

# Copyright 2013 Red Hat, Inc.
# All Rights Reserved.
#
# 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 shutil
from six.moves import urllib
from tempest.lib import exceptions
from config_tempest import constants as C
from config_tempest.services.base import VersionedService
class ImageService(VersionedService):
def __init__(self, name, service_url, token, disable_ssl_validation,
client=None):
super(ImageService, self).__init__(name, service_url, token,
disable_ssl_validation,
client)
def set_image_preferences(self, disk_format, non_admin):
"""Sets image prefferences.
:type disk_format: string
:type non_admin: bool
"""
self.disk_format = disk_format
self.non_admin = non_admin
def set_default_tempest_options(self, conf):
# When cirros is the image, set validation.image_ssh_user to cirros.
# The option is heavily used in CI and it's also usefull for refstack,
# because we don't have to specify overrides.
if 'cirros' in conf.get_defaulted('image',
'image_path').rsplit('/')[-1]:
conf.set('validation', 'image_ssh_user', 'cirros')
# image.http_image is a tempest option which defines 'http accessible
# image', it can be in a compressed format so it can't be mistaken
# for an image which will be uploaded to the glance.
# image.http_image and image.image_path can be 2 different images.
# If image.http_image wasn't set as an override, it will be set to
# image.image_path or to DEFAULT_IMAGE
image_path = conf.get_defaulted('image', 'image_path')
if self._find_image_by_name(image_path) is None:
conf.set('image', 'http_image', image_path)
else:
# image.image_path is name of the image already present in glance,
# this value can't be set to image.http_image, therefor set the
# default value
conf.set('image', 'http_image', C.DEFAULT_IMAGE)
def get_supported_versions(self):
return ['v1', 'v2']
@staticmethod
def get_service_name():
return ['glance']
def get_catalog(self):
return 'image'
def set_versions(self):
super(ImageService, self).set_versions(top_level=False)
def create_tempest_images(self, conf):
"""Uploads an image to the glance.
The method creates images specified in conf, if they're not created
already. Then it sets their IDs to the conf.
:type conf: TempestConf object
"""
img_dir = os.path.join(conf.get("scenario", "img_dir"))
image_path = conf.get_defaulted('image', 'image_path')
img_path = os.path.join(img_dir,
os.path.basename(image_path))
name = image_path[image_path.rfind('/') + 1:]
if not os.path.exists(img_dir):
try:
os.makedirs(img_dir)
except OSError:
raise
conf.set('scenario', 'img_file', name)
alt_name = name + "_alt"
image_id = None
if conf.has_option('compute', 'image_ref'):
image_id = conf.get('compute', 'image_ref')
image_id = self.find_or_upload_image(image_id, name,
image_source=image_path,
image_dest=img_path)
alt_image_id = None
if conf.has_option('compute', 'image_ref_alt'):
alt_image_id = conf.get('compute', 'image_ref_alt')
alt_image_id = self.find_or_upload_image(alt_image_id, alt_name,
image_source=image_path,
image_dest=img_path)
conf.set('compute', 'image_ref', image_id)
conf.set('compute', 'image_ref_alt', alt_image_id)
def find_or_upload_image(self, image_id, image_name, image_source='',
image_dest=''):
"""If the image is not found, uploads it.
:type image_id: string
:type image_name: string
:type image_source: string
:type image_dest: string
"""
image = self._find_image(image_id, image_name)
if image:
C.LOG.info("(no change) Found image '%s'", image['name'])
path = os.path.abspath(image_dest)
if not os.path.isfile(path):
self._download_image(image['id'], path)
else:
C.LOG.info("Creating image '%s'", image_name)
if image_source.startswith("http:") or \
image_source.startswith("https:"):
self._download_file(image_source, image_dest)
else:
try:
shutil.copyfile(image_source, image_dest)
except IOError:
# let's try if this is the case when a user uses already
# existing image in glance which is not uploaded as *_alt
if image_name[-4:] == "_alt":
image = self._find_image(None, image_name[:-4])
if image:
path = os.path.abspath(image_dest)
if not os.path.isfile(path):
self._download_image(image['id'], path)
else:
raise IOError
image = self._upload_image(image_name, image_dest)
return image['id']
def _find_image(self, image_id, image_name):
"""Find image by ID or name (the image client doesn't have this).
:type image_id: string
:type image_name: string
"""
if image_id:
try:
return self.client.show_image(image_id)
except exceptions.NotFound:
pass
return self._find_image_by_name(image_name)
def _find_image_by_name(self, image_name):
"""Find image by name.
:type image_name: string
:return: Information in a dict about the found image
:rtype: dict or None if image was not found
"""
for x in self.client.list_images()['images']:
if x['name'] == image_name:
return x
return None
def _upload_image(self, name, path):
"""Upload image file from `path` into Glance with `name`.
:type name: string
:type path: string
"""
C.LOG.info("Uploading image '%s' from '%s'",
name, os.path.abspath(path))
if self.non_admin:
visibility = 'community'
else:
visibility = 'public'
with open(path, 'rb') as data:
image = self.client.create_image(name=name,
disk_format=self.disk_format,
container_format='bare',
visibility=visibility)
self.client.store_image_file(image['id'], data)
return image
def _download_image(self, id, path):
"""Download image from glance.
:type id: string
:type path: string
"""
C.LOG.info("Downloading image %s to %s", id, path)
body = self.client.show_image_file(id)
C.LOG.debug(type(body.data))
with open(path, 'wb') as out:
out.write(body.data)
def _download_file(self, url, destination):
"""Downloads a file specified by `url` to `destination`.
:type url: string
:type destination: string
"""
if os.path.exists(destination):
C.LOG.info("Image '%s' already fetched to '%s'.", url, destination)
return
C.LOG.info("Downloading '%s' and saving as '%s'", url, destination)
f = urllib.request.urlopen(url)
data = f.read()
with open(destination, "wb") as dest:
dest.write(data)