"Fossies" - the Fresh Open Source Software Archive

Member "freezer-10.0.0/freezer/openstack/osclients.py" (14 Apr 2021, 21478 Bytes) of package /linux/misc/openstack/freezer-10.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "osclients.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 9.0.0_vs_10.0.0.

    1 # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P.
    2 # (c) Copyright 2016 Hewlett-Packard Enterprise Development Company, L.P.
    3 #
    4 # Licensed under the Apache License, Version 2.0 (the "License");
    5 # you may not use this file except in compliance with the License.
    6 # You may obtain a copy of the License at
    7 #
    8 #     http://www.apache.org/licenses/LICENSE-2.0
    9 #
   10 # Unless required by applicable law or agreed to in writing, software
   11 # distributed under the License is distributed on an "AS IS" BASIS,
   12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13 # See the License for the specific language governing permissions and
   14 # limitations under the License.
   15 
   16 import os
   17 import time
   18 
   19 from cinderclient import client as cinder_client
   20 from glanceclient import client as glance_client
   21 from keystoneauth1 import loading
   22 from keystoneauth1 import session
   23 from neutronclient.v2_0 import client as neutron_client
   24 from novaclient import client as nova_client
   25 from oslo_config import cfg
   26 from oslo_log import log
   27 import swiftclient
   28 
   29 from freezer.utils import utils
   30 
   31 CONF = cfg.CONF
   32 LOG = log.getLogger(__name__)
   33 
   34 
   35 class OSClientManager(object):
   36 
   37     def __init__(self, auth_url, auth_method='password', **kwargs):
   38         self.swift = None
   39         self.glance = None
   40         self.nova = None
   41         self.cinder = None
   42         self.neutron = None
   43         self.dry_run = kwargs.pop('dry_run', None)
   44         loader = loading.get_plugin_loader(auth_method)
   45         # copy the args for swift authentication !
   46         self.swift_args = kwargs.copy()
   47         self.swift_args['auth_url'] = auth_url
   48         # client specific arguments !
   49         self.client_kwargs = {}
   50         # session specific arguments
   51         session_kwargs = {}
   52         if 'verify' in kwargs.keys():
   53             session_kwargs['verify'] = kwargs.pop('verify')
   54         if 'cacert' in kwargs.keys():
   55             session_kwargs['verify'] = kwargs.pop('cacert')
   56         # client specific args
   57         if 'insecure' in kwargs.keys():
   58             self.client_kwargs['insecure'] = kwargs.pop('insecure')
   59             session_kwargs['verify'] = False
   60         if 'region_name' in kwargs.keys():
   61             self.client_kwargs['region_name'] = kwargs.pop('region_name')
   62         if 'endpoint_type' in kwargs.keys():
   63             self.client_kwargs['endpoint_type'] = kwargs.pop('endpoint_type')
   64         if 'identity_api_version' in kwargs.keys():
   65             kwargs.pop('identity_api_version')
   66         if 'auth_version' in kwargs.keys():
   67             kwargs.pop('auth_version')
   68         if 'interface' in kwargs.keys():
   69             self.client_kwargs['interface'] = kwargs.pop('interface')
   70 
   71         self.compute_version = kwargs.pop('compute_api_version', 2)
   72         self.image_version = kwargs.pop('image_api_version', 2)
   73         self.volume_version = kwargs.pop('volume_api_version', 3)
   74         self.neutron_version = kwargs.pop('neutron_api_version', 2)
   75         self.auth = loader.load_from_options(auth_url=auth_url, **kwargs)
   76 
   77         self.sess = session.Session(auth=self.auth, **session_kwargs)
   78 
   79     def create_nova(self):
   80         """
   81         Use pre-initialized session to create an instance of nova client.
   82         :return: novaclient instance
   83         """
   84         self.nova = nova_client.Client(self.compute_version, session=self.sess,
   85                                        **self.client_kwargs)
   86         return self.nova
   87 
   88     def create_neutron(self):
   89         """
   90         Use pre-initialized session to create an instance of neutron client.
   91         :return: neutronclient instance
   92         """
   93         self.neutron = neutron_client.Client(session=self.sess,
   94                                              **self.client_kwargs)
   95         return self.neutron
   96 
   97     def create_glance(self):
   98         """
   99         Use pre-initialized session to create an instance of glance client.
  100         :return: glanceclient instance
  101         """
  102         if 'endpoint_type' in self.client_kwargs.keys():
  103             self.client_kwargs.pop('endpoint_type')
  104         if 'insecure' in self.client_kwargs.keys():
  105             self.client_kwargs.pop('insecure')
  106 
  107         self.glance = glance_client.Client(self.image_version,
  108                                            session=self.sess,
  109                                            **self.client_kwargs)
  110         return self.glance
  111 
  112     def create_cinder(self):
  113         """
  114         Use pre-initialized session to create an instance of cinder client.
  115         :return: cinderclient instance
  116         """
  117         self.cinder = cinder_client.Client(self.volume_version,
  118                                            session=self.sess,
  119                                            **self.client_kwargs)
  120         return self.cinder
  121 
  122     def create_swift(self):
  123         """
  124         Swift client needs to be treated differently so we need to copy the
  125         arguments and provide it to swiftclient the correct way !
  126         :return: swiftclient instance
  127         """
  128         os_options = {}
  129         auth_version = None
  130         if 'region_name' in self.swift_args.keys():
  131             os_options['region_name'] = self.swift_args.get('region_name')
  132         if 'endpoint_type' in self.swift_args.keys():
  133             os_options['endpoint_type'] = self.swift_args.get('endpoint_type')
  134         if 'identity_api_version' in self.swift_args.keys():
  135             os_options['identity_api_version'] = \
  136                 self.swift_args.get('identity_api_version')
  137             auth_version = os_options['identity_api_version']
  138 
  139         if 'token' in self.swift_args.keys():
  140             os_options['auth_token'] = self.swift_args.get('token')
  141         if 'auth_version' in self.swift_args.keys():
  142             auth_version = self.swift_args.get('auth_version')
  143         os_options['project_domain_name'] = \
  144             self.swift_args.get('project_domain_name')
  145         os_options['user_domain_name'] = \
  146             self.swift_args.get('user_domain_name')
  147         os_options['project_domain_id'] = \
  148             self.swift_args.get('project_domain_id')
  149         os_options['user_domain_id'] = self.swift_args.get('user_domain_id')
  150         os_options['project_id'] = self.swift_args.get('project_id')
  151 
  152         tenant_name = self.swift_args.get('project_name') or self.swift_args.\
  153             get('tenant_name')
  154         self.swift = swiftclient.client.Connection(
  155             authurl=self.swift_args.get('auth_url'),
  156             user=self.swift_args.get('username'),
  157             key=self.swift_args.get('password'),
  158             tenant_name=tenant_name,
  159             insecure=self.swift_args.get('insecure', False),
  160             cacert=self.swift_args.get('cacert', None),
  161             os_options=os_options,
  162             auth_version=auth_version
  163         )
  164 
  165         if self.dry_run:
  166             self.swift = DryRunSwiftclientConnectionWrapper(self.swift)
  167         return self.swift
  168 
  169     def get_nova(self):
  170         """
  171         Get novaclient instance
  172         :return: novaclient instance
  173         """
  174         if not self.nova:
  175             self.nova = self.create_nova()
  176         return self.nova
  177 
  178     def get_neutron(self):
  179         """
  180         Get neutronclient instance
  181         :return: neutronclient instance
  182         """
  183         if not self.neutron:
  184             self.neutron = self.create_neutron()
  185         return self.neutron
  186 
  187     def get_glance(self):
  188         """
  189         Get glanceclient instance
  190         :return: glanceclient instance
  191         """
  192         if not self.glance:
  193             self.glance = self.create_glance()
  194         return self.glance
  195 
  196     def get_cinder(self):
  197         """
  198         Get cinderclient instance
  199         :return: cinderclient instance
  200         """
  201         if not self.cinder:
  202             self.cinder = self.create_cinder()
  203         return self.cinder
  204 
  205     def get_swift(self):
  206         """
  207         Get swiftclient instance
  208         :return: swiftclient instance
  209         """
  210         if not self.swift:
  211             self.swift = self.create_swift()
  212         return self.swift
  213 
  214     def provide_snapshot(self, volume, snapshot_name):
  215         """
  216         Creates snapshot for cinder volume with --force parameter
  217         :param volume: volume object for snapshoting
  218         :param snapshot_name: name of snapshot
  219         :return: snapshot object
  220         """
  221         snapshot = self.get_cinder().volume_snapshots.create(
  222             volume_id=volume.id,
  223             name=snapshot_name,
  224             force=True)
  225 
  226         LOG.debug("Snapshot for volume with id {0}".format(volume.id))
  227 
  228         while snapshot.status != "available":
  229             try:
  230                 LOG.debug("Snapshot status: " + snapshot.status)
  231                 snapshot = self.get_cinder().volume_snapshots.get(snapshot.id)
  232                 if snapshot.status == "error":
  233                     raise RuntimeError("snapshot has error state")
  234                 time.sleep(5)
  235             except RuntimeError:
  236                 LOG.info("Delete snapshot in error state " + snapshot.id)
  237                 self.get_cinder().volume_snapshots.delete(snapshot)
  238                 raise Exception("Delete snapshot in error"
  239                                 " state " + snapshot.id)
  240             except Exception as e:
  241                 LOG.exception(e)
  242         return snapshot
  243 
  244     def do_copy_volume(self, snapshot):
  245         """
  246         Creates new volume from a snapshot
  247         :param snapshot: provided snapshot
  248         :return: created volume
  249         """
  250         volume = self.get_cinder().volumes.create(
  251             size=snapshot.size,
  252             snapshot_id=snapshot.id)
  253 
  254         while volume.status != "available":
  255             try:
  256                 LOG.info("Volume copy status: " + volume.status)
  257                 volume = self.get_cinder().volumes.get(volume.id)
  258                 if volume.status == "error":
  259                     raise RuntimeError("Volume copy has error state")
  260                 time.sleep(5)
  261             except RuntimeError:
  262                 LOG.info("Delete volume in error state " + volume.id)
  263                 self.get_cinder().volumes.delete(volume.id)
  264                 raise Exception("Delete volume in error state " + volume.id)
  265             except Exception as e:
  266                 LOG.exception(e)
  267                 LOG.warning("Exception getting volume status")
  268         return volume
  269 
  270     def make_glance_image(self, image_volume_name, copy_volume):
  271         """
  272         Creates an glance image from volume
  273         :param image_volume_name: Name of image
  274         :param copy_volume: volume to make an image
  275         :return: Glance image object
  276         """
  277         image_id = self.get_cinder().volumes.upload_to_image(
  278             volume=copy_volume,
  279             force=True,
  280             image_name=image_volume_name,
  281             container_format="bare",
  282             disk_format="raw")[1]["os-volume_upload_image"]["image_id"]
  283         image = self.get_glance().images.get(image_id)
  284         while image.status != "active":
  285             try:
  286                 time.sleep(5)
  287                 LOG.info("Image status: " + image.status)
  288                 image = self.get_glance().images.get(image.id)
  289                 if image.status in ("killed", "deleted"):
  290                     raise RuntimeError("Image in killed or deleted state")
  291             except RuntimeError:
  292                 if image.status == 'killed':
  293                     LOG.info("Delete image in killed state " + image_id)
  294                     self.get_glance().images.delete(image_id)
  295                 raise Exception("Delete image in killed state " + image_id)
  296             except Exception as e:
  297                 if hasattr(e, 'code') and e.code == 404:
  298                     LOG.warning('Image is not found ' + image_id)
  299                     raise Exception('Image is not found ' + image_id)
  300                 LOG.exception(e)
  301                 LOG.warning("Exception getting image status")
  302         return image
  303 
  304     def clean_snapshot(self, snapshot):
  305         """
  306         Deletes snapshot
  307         :param snapshot: snapshot name
  308         """
  309         LOG.info("Deleting existed snapshot: " + snapshot.id)
  310         self.get_cinder().volume_snapshots.delete(snapshot)
  311 
  312     def download_image(self, image):
  313         """
  314         Creates a stream for image data
  315         :param image: Image object for downloading
  316         :return: stream of image data
  317         """
  318         LOG.debug("Download image enter")
  319         stream = self.get_glance().images.data(image.id)
  320         LOG.debug("Stream with size {0}".format(image.size))
  321         return utils.ReSizeStream(stream, image.size,
  322                                   CONF.get('max_segment_size'))
  323 
  324     def create_image(self, name, container_format, disk_format, data=None):
  325         LOG.info("Creating glance image")
  326         glance = self.get_glance()
  327         image = glance.images.create(name=name,
  328                                      container_format=container_format,
  329                                      disk_format=disk_format)
  330         if image is None:
  331             msg = "Failed to create glance image {}".format(name)
  332             LOG.error(msg)
  333             raise BaseException(msg)
  334         if data is None:
  335             return image
  336         glance.images.upload(image.id, data)
  337         while image.status not in ('active', 'killed'):
  338             LOG.info("Waiting for glance image upload")
  339             time.sleep(5)
  340             image = glance.images.get(image.id)
  341         if image.status == 'killed':
  342             raise BaseException('Failed to upload data into image')
  343         LOG.info("Created glance image {}".format(image.id))
  344         return image
  345 
  346 
  347 class OpenstackOpts(object):
  348     """
  349     Gathering and maintaining the right Openstack credentials that will be used
  350     to authenticate against keystone. Now we support keystone v3.
  351     We need to provide a correct url that ends with either  v3
  352     or provide auth_version or identity_api_version
  353     """
  354     def __init__(self, auth_url, auth_method='password', auth_version=None,
  355                  username=None, password=None, region_name=None, cacert=None,
  356                  identity_api_version=None, project_id=None, project_name=None,
  357                  token=None, insecure=False,
  358                  endpoint_type='internalURL', interface=None,
  359                  compute_api_version=2, image_api_version=2,
  360                  volume_api_version=3, user_domain_name=None, domain_id=None,
  361                  user_domain_id=None, project_domain_id=None, domain_name=None,
  362                  project_domain_name=None):
  363         """
  364         Authentication Options to build a valid opts dict to be used to
  365         authenticate against keystone. You must provide auth_url with a vaild
  366         Openstack version at the end  v3 or provide auth_version.
  367         :param auth_url: string Keystone API URL
  368         :param auth_method: string defaults to password or token (not tested)
  369         :param auth_version: string Keystone API version. v3
  370         :param username: string A valid Username
  371         :param password: string A valid Password
  372         :param region_name: string Region name or None
  373         :param cacert: string Path to CA certificate
  374         :param identity_api_version: string Keystone API version to use
  375         :param project_id: UUID string Project ID
  376         :param project_name: string Project Name
  377         :param token: string Valid token. Only if auth_method is token
  378         :param insecure: boolean Use insecure connections
  379         :param endpoint_type: string publicURL, adminURL, internalURL
  380         :param interface: string internal, ...
  381         :param compute_api_version: int NOVA API version to use default 2
  382         :param image_api_version: int Glance API version, default 2
  383         :param volume_api_version: int Cinder API version, default 3
  384         :param user_domain_name: string User Domain Name. only with keystone v3
  385         :param domain_id: string Domain ID. Only with keystone v3
  386         :param user_domain_id: string User Domain ID. only with keystone v3
  387         :param project_domain_id: string Project Domain ID. keystone v3 only
  388         :param domain_name: string Domain Name. only with keystone v3
  389         :param project_domain_name: string Project Domain Name.
  390                keystone v3 only
  391         :return: None
  392         """
  393         self.auth_url = auth_url
  394         self.auth_method = auth_method
  395         self.auth_version = auth_version
  396         self.username = username
  397         self.password = password
  398         self.region_name = region_name
  399         self.cacert = cacert
  400         self.identity_api_version = identity_api_version
  401         self.project_id = project_id
  402         self.project_name = project_name
  403         self.token = token
  404         self.insecure = insecure
  405         self.endpoint_type = endpoint_type
  406         self.interface = interface
  407         self.compute_api_version = compute_api_version
  408         self.image_api_version = image_api_version
  409         self.volume_api_version = volume_api_version
  410         self.user_domain_id = user_domain_id
  411         self.user_domain_name = user_domain_name
  412         self.project_domain_id = project_domain_id
  413         self.project_domain_name = project_domain_name
  414         self.domain_id = domain_id
  415         self.domain_name = domain_name
  416         if auth_url is None:
  417             raise Exception('auth_url required to authenticate. Make sure to '
  418                             'export OS_AUTH_URL=http://keystone_url:5000/v3')
  419         if auth_version is None and identity_api_version is None:
  420             version = auth_url.rstrip('/').rsplit('/')[-1]
  421             if version == 'v3':
  422                 self.auth_version = self.identity_api_version = str('3')
  423             elif version == 'v2.0':
  424                 self.auth_version = self.identity_api_version = str('2.0')
  425             else:
  426                 raise Exception('Keystone Auth version {0} is not supported!. '
  427                                 'Generated from auth_url: {1}'
  428                                 .format(version, auth_url))
  429 
  430         LOG.info('Authenticating with Keystone version: '
  431                  '{0}, auth_url: {1}, username: {2}, project: {3}'.
  432                  format(self.auth_version, self.auth_url,
  433                         self.username, self.project_name))
  434 
  435     def get_opts_dicts(self):
  436         """
  437         Return openstack auth arguments as dict
  438         detects the auth version from url if not provided
  439         handles certificate issues
  440         """
  441         opts = self.__dict__
  442         if self.auth_method == 'password':
  443             opts.pop('token', None)
  444         elif self.auth_method == 'token':
  445             opts.pop('username', None)
  446             opts.pop('password', None)
  447 
  448         if not self.cacert:
  449             opts['verify'] = False
  450             opts['insecure'] = True
  451         self.auth_version = str(self.auth_version)
  452         self.identity_api_version = str(self.identity_api_version)
  453 
  454         opts['auth_version'] = opts['identity_api_version'] = '3'
  455 
  456         for i in opts.copy().keys():
  457             if opts.get(i) is None:
  458                 opts.pop(i)
  459         return opts
  460 
  461     @staticmethod
  462     def create_from_env():
  463         """
  464         Parse environment variables and load Openstack related options.
  465         :return:
  466         """
  467         return OpenstackOpts.create_from_dict(os.environ)
  468 
  469     @staticmethod
  470     def create_from_dict(src_dict):
  471         """
  472         Load Openstack arguments from dict and return OpenstackOpts object with
  473         the correct parameters to authenticate.
  474         :param src_dict: dict
  475         :return: OpenstackOpts object with the passed arguments in place
  476         """
  477         return OpenstackOpts(
  478             auth_url=src_dict.get('OS_AUTH_URL'),
  479             auth_method=src_dict.get('OS_AUTH_METHOD', 'password'),
  480             auth_version=src_dict.get('OS_AUTH_VERSION', None),
  481             username=src_dict.get('OS_USERNAME', None),
  482             password=src_dict.get('OS_PASSWORD', None),
  483             project_id=src_dict.get('OS_PROJECT_ID', None),
  484             project_name=src_dict.get('OS_PROJECT_NAME', None),
  485             region_name=src_dict.get('OS_REGION_NAME', None),
  486             endpoint_type=src_dict.get('OS_ENDPOINT_TYPE', 'publicURL'),
  487             cacert=src_dict.get('OS_CACERT', None),
  488             identity_api_version=src_dict.get('OS_IDENTITY_API_VERSION', None),
  489             insecure=src_dict.get('OS_INSECURE', CONF.get('insecure', False)),
  490             token=src_dict.get('OS_TOKEN', None),
  491             interface=src_dict.get('OS_INTERFACE', None),
  492             user_domain_name=src_dict.get('OS_USER_DOMAIN_NAME', None),
  493             user_domain_id=src_dict.get('OS_USER_DOMAIN_ID', None),
  494             project_domain_id=src_dict.get('OS_PROJECT_DOMAIN_ID', None),
  495             project_domain_name=src_dict.get('OS_PROJECT_DOMAIN_NAME', None),
  496             domain_id=src_dict.get('OS_DOMAIN_ID'),
  497             domain_name=src_dict.get('OS_DOMAIN_NAME'),
  498             compute_api_version=src_dict.get('OS_COMPUTE_API_VERSION', 2),
  499             volume_api_version=src_dict.get('OS_VOLUME_API_VERSION', 3),
  500             image_api_version=src_dict.get('OS_IMAGE_API_VERSION', 2)
  501         )
  502 
  503 
  504 class DryRunSwiftclientConnectionWrapper(object):
  505     def __init__(self, sw_connector):
  506         self.sw_connector = sw_connector
  507         self.get_object = sw_connector.get_object
  508         self.get_account = sw_connector.get_account
  509         self.get_container = sw_connector.get_container
  510         self.head_object = sw_connector.head_object
  511         self.put_object = self.dummy
  512         self.put_container = self.dummy
  513         self.delete_object = self.dummy
  514 
  515     def dummy(self, *args, **kwargs):
  516         pass