text: #   Copyright 2014 IBM Corp.
  #
  #   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 oslo_log import log as logging
  from oslo_utils import excutils
  import taskflow.engines
  from taskflow.patterns import linear_flow
  
  from cinder import exception
  from cinder import flow_utils
  from cinder.i18n import _
  from cinder.volume.flows.api import create_volume as create_api
  from cinder.volume.flows import common as flow_common
  from cinder.volume.flows.manager import create_volume as create_mgr
  
  LOG = logging.getLogger(__name__)
  
  ACTION = 'volume:manage_existing'
  
  
  class PrepareForQuotaReservationTask(flow_utils.CinderTask):
      """Gets the volume size from the driver."""
  
      default_provides = set(['size', 'volume_type_id', 'volume_properties',
                              'volume_spec'])
  
      def __init__(self, db, driver):
          super(PrepareForQuotaReservationTask, self).__init__(addons=[ACTION])
          self.db = db
          self.driver = driver
  
      def execute(self, context, volume, manage_existing_ref):
          driver_name = self.driver.__class__.__name__
          if not self.driver.initialized:
              LOG.error("Unable to manage existing volume. "
                        "Volume driver %s not initialized.", driver_name)
              flow_common.error_out(volume, _("Volume driver %s not "
                                              "initialized.") % driver_name,
                                    status='error_managing')
              raise exception.DriverNotInitialized()
  
          size = 0
          try:
              size = self.driver.manage_existing_get_size(volume,
                                                          manage_existing_ref)
          except Exception:
              with excutils.save_and_reraise_exception():
                  reason = _("Volume driver %s get exception.") % driver_name
                  flow_common.error_out(volume, reason,
                                        status='error_managing')
  
          return {'size': size,
                  'volume_type_id': volume.volume_type_id,
                  'volume_properties': volume,
                  'volume_spec': {'status': volume.status,
                                  'volume_name': volume.name,
                                  'volume_id': volume.id}}
  
      def revert(self, context, result, flow_failures, volume, **kwargs):
          reason = _('Volume manage failed.')
          flow_common.error_out(volume, reason=reason,
                                status='error_managing')
          LOG.error("Volume %s: manage failed.", volume.id)
  
  
  class ManageExistingTask(flow_utils.CinderTask):
      """Brings an existing volume under Cinder management."""
  
      default_provides = set(['volume'])
  
      def __init__(self, db, driver):
          super(ManageExistingTask, self).__init__(addons=[ACTION])
          self.db = db
          self.driver = driver
  
      def execute(self, context, volume, manage_existing_ref, size):
          model_update = self.driver.manage_existing(volume,
                                                     manage_existing_ref)
  
          if not model_update:
              model_update = {}
          model_update.update({'size': size})
          try:
              volume.update(model_update)
              volume.save()
          except exception.CinderException:
              LOG.exception("Failed updating model of volume %(volume_id)s"
                            " with creation provided model %(model)s",
                            {'volume_id': volume.id,
                             'model': model_update})
              raise
  
          return {'volume': volume}
  
  
  def get_flow(context, db, driver, host, volume, ref):
      """Constructs and returns the manager entrypoint flow."""
  
      flow_name = ACTION.replace(":", "_") + "_manager"
      volume_flow = linear_flow.Flow(flow_name)
  
      # This injects the initial starting flow values into the workflow so that
      # the dependency order of the tasks provides/requires can be correctly
      # determined.
      create_what = {
          'context': context,
          'volume': volume,
          'manage_existing_ref': ref,
          'group_snapshot': None,
          'optional_args': {'is_quota_committed': False, 'update_size': True}
      }
  
      volume_flow.add(create_mgr.NotifyVolumeActionTask(db,
                                                        "manage_existing.start"),
                      PrepareForQuotaReservationTask(db, driver),
                      create_api.QuotaReserveTask(),
                      ManageExistingTask(db, driver),
                      create_api.QuotaCommitTask(),
                      create_mgr.CreateVolumeOnFinishTask(db,
                                                          "manage_existing.end"))
  
      # Now load (but do not run) the flow using the provided initial data.
      return taskflow.engines.load(volume_flow, store=create_what)
  