text: # -*- coding: utf-8 -*-
  '''
  Manage JBoss 7 Application Server via CLI interface
  
  This state uses jboss-cli.sh script from JBoss installation and parses its output to determine execution result.
  
  In order to run each state, jboss_config dictionary with the following properties must be passed:
  
  .. code-block:: yaml
  
     jboss:
        cli_path: '/opt/jboss/jboss-7.0/bin/jboss-cli.sh'
        controller: 10.11.12.13:9999
        cli_user: 'jbossadm'
        cli_password: 'jbossadm'
  
  If controller doesn't require password, then passing cli_user and cli_password parameters is not obligatory.
  
  Example of application deployment:
  
  .. code-block:: yaml
  
       application_deployed:
        jboss7.deployed:
         - artifact:
             artifactory_url: http://artifactory.intranet.company.com/artifactory
             repository: 'ext-release-local'
             artifact_id: 'webcomponent'
             group_id: 'com.company.application'
             packaging: 'war'
             version: '0.1'
             target_dir: '/tmp'
          - jboss_config:
             cli_path: '/opt/jboss/jboss-7.0/bin/jboss-cli.sh'
             controller: 10.11.12.13:9999
             cli_user: 'jbossadm'
             cli_password: 'jbossadm'
  
  Since same dictionary with configuration will be used in all the states, it is much more convenient to move jboss configuration and other properties
  to pillar. For example, configuration of jboss server, artifactory address and application version could be moved to pillars:
  
  .. code-block:: yaml
  
       application_deployed:
        jboss7.deployed:
         - artifact:
             artifactory_url: {{ pillar['artifactory']['url'] }}
             repository: {{ pillar['artifactory']['repository'] }}
             artifact_id: 'webcomponent'
             group_id: 'com.company.application'
             packaging: 'war'
             version: {{ pillar['webcomponent-artifact']['version'] }}
             latest_snapshot: {{ pillar['webcomponent-artifact']['latest_snapshot'] }}
             repository: {{ pillar['webcomponent-artifact']['repository'] }}
         - jboss_config: {{ pillar['jboss'] }}
  
  
  Configuration in pillars:
  
  .. code-block:: yaml
  
     artifactory:
        url: 'http://artifactory.intranet.company.com/artifactory'
        repository: 'libs-snapshots-local'
  
     webcomponent-artifact:
        repository: 'libs-snapshots-local'
        latest_snapshot: True
        version: -1 #If latest_snapshot then version is ignored
  
  For the sake of brevity, examples for each state assume that jboss_config is moved to pillars.
  
  
  '''
  
  # Import python libs
  from __future__ import absolute_import
  import time
  import logging
  import re
  import traceback
  
  # Import Salt libs
  from salt.utils import dictdiffer
  from salt.exceptions import CommandExecutionError
  
  # Import 3rd-party libs
  import salt.ext.six as six
  
  log = logging.getLogger(__name__)
  
  
  def datasource_exists(name, jboss_config, datasource_properties, recreate=False):
      '''
      Ensures that a datasource with given properties exist on the jboss instance.
      If datasource doesn't exist, it is created, otherwise only the properties that are different will be updated.
  
      name
          Datasource property name
      jboss_config
          Dict with connection properties (see state description)
      datasource_properties
          Dict with datasource properties
      recreate : False
          If set to True and datasource exists it will be removed and created again. However, if there are deployments that depend on the datasource, it will not me possible to remove it.
  
      Example:
  
      .. code-block:: yaml
  
          sampleDS:
            jboss7.datasource_exists:
             - recreate: False
             - datasource_properties:
                 driver-name: mysql
                 connection-url: 'jdbc:mysql://localhost:3306/sampleDatabase'
                 jndi-name: 'java:jboss/datasources/sampleDS'
                 user-name: sampleuser
                 password: secret
                 min-pool-size: 3
                 use-java-context: True
             - jboss_config: {{ pillar['jboss'] }}
  
      '''
      log.debug(" ======================== STATE: jboss7.datasource_exists (name: %s) ", name)
      ret = {'name': name,
             'result': True,
             'changes': {},
             'comment': ''}
  
      has_changed = False
      ds_current_properties = {}
      ds_result = __salt__['jboss7.read_datasource'](jboss_config=jboss_config, name=name)
      if ds_result['success']:
          ds_current_properties = ds_result['result']
          if recreate:
              remove_result = __salt__['jboss7.remove_datasource'](jboss_config=jboss_config, name=name)
              if remove_result['success']:
                  ret['changes']['removed'] = name
              else:
                  ret['result'] = False
                  ret['comment'] = 'Could not remove datasource. Stdout: '+remove_result['stdout']
                  return ret
  
              has_changed = True  # if we are here, we have already made a change
  
              create_result = __salt__['jboss7.create_datasource'](jboss_config=jboss_config, name=name, datasource_properties=datasource_properties)
              if create_result['success']:
                  ret['changes']['created'] = name
              else:
                  ret['result'] = False
                  ret['comment'] = 'Could not create datasource. Stdout: '+create_result['stdout']
                  return ret
  
              read_result = __salt__['jboss7.read_datasource'](jboss_config=jboss_config, name=name)
              if read_result['success']:
                  ds_new_properties = read_result['result']
              else:
                  ret['result'] = False
                  ret['comment'] = 'Could not read datasource. Stdout: '+read_result['stdout']
                  return ret
  
          else:
              update_result = __salt__['jboss7.update_datasource'](jboss_config=jboss_config, name=name, new_properties=datasource_properties)
              if not update_result['success']:
                  ret['result'] = False
                  ret['comment'] = 'Could not update datasource. '+update_result['comment']
                  # some changes to the datasource may have already been made, therefore we don't quit here
              else:
                  ret['comment'] = 'Datasource updated.'
  
              read_result = __salt__['jboss7.read_datasource'](jboss_config=jboss_config, name=name)
              ds_new_properties = read_result['result']
      else:
          if ds_result['err_code'] == 'JBAS014807':  # ok, resource not exists:
              create_result = __salt__['jboss7.create_datasource'](jboss_config=jboss_config, name=name, datasource_properties=datasource_properties)
              if create_result['success']:
                  read_result = __salt__['jboss7.read_datasource'](jboss_config=jboss_config, name=name)
                  ds_new_properties = read_result['result']
                  ret['comment'] = 'Datasource created.'
              else:
                  ret['result'] = False
                  ret['comment'] = 'Could not create datasource. Stdout: '+create_result['stdout']
          else:
              raise CommandExecutionError('Unable to handle error', ds_result['failure-description'])
  
      if ret['result']:
          log.debug("ds_new_properties=%s", str(ds_new_properties))
          log.debug("ds_current_properties=%s", str(ds_current_properties))
          diff = dictdiffer.diff(ds_new_properties, ds_current_properties)
  
          added = diff.added()
          if len(added) > 0:
              has_changed = True
              ret['changes']['added'] = __format_ds_changes(added, ds_current_properties, ds_new_properties)
  
          removed = diff.removed()
          if len(removed) > 0:
              has_changed = True
              ret['changes']['removed'] = __format_ds_changes(removed, ds_current_properties, ds_new_properties)
  
          changed = diff.changed()
          if len(changed) > 0:
              has_changed = True
              ret['changes']['changed'] = __format_ds_changes(changed, ds_current_properties, ds_new_properties)
  
          if not has_changed:
              ret['comment'] = 'Datasource not changed.'
  
      return ret
  
  
  def __format_ds_changes(keys, old_dict, new_dict):
      log.debug("__format_ds_changes(keys=%s, old_dict=%s, new_dict=%s)", str(keys), str(old_dict), str(new_dict))
      changes = ''
      for key in keys:
          log.debug("key=%s", str(key))
          if key in old_dict and key in new_dict:
              changes += key+':'+__get_ds_value(old_dict, key)+'->'+__get_ds_value(new_dict, key)+'\n'
          elif key in old_dict:
              changes += key+'\n'
          elif key in new_dict:
              changes += key+':'+__get_ds_value(new_dict, key)+'\n'
      return changes
  
  
  def __get_ds_value(dct, key):
      log.debug("__get_value(dict,%s)", key)
      if key == "password":
          return "***"
      elif dct[key] is None:
          return 'undefined'
      else:
          return str(dct[key])
  
  
  def bindings_exist(name, jboss_config, bindings):
      '''
      Ensures that given JNDI binding are present on the server.
      If a binding doesn't exist on the server it will be created.
      If it already exists its value will be changed.
  
      jboss_config:
          Dict with connection properties (see state description)
      bindings:
          Dict with bindings to set.
  
      Example:
  
      .. code-block:: yaml
  
              jndi_entries_created:
                jboss7.bindings_exist:
                 - bindings:
                    'java:global/sampleapp/environment': 'DEV'
                    'java:global/sampleapp/configurationFile': '/var/opt/sampleapp/config.properties'
                 - jboss_config: {{ pillar['jboss'] }}
  
      '''
      log.debug(" ======================== STATE: jboss7.bindings_exist (name: %s) ", name)
      log.debug('bindings='+str(bindings))
      ret = {'name': name,
             'result': True,
             'changes': {},
             'comment': 'Bindings not changed.'}
  
      has_changed = False
      for key in bindings:
          value = str(bindings[key])
          query_result = __salt__['jboss7.read_simple_binding'](binding_name=key, jboss_config=jboss_config)
          if query_result['success']:
              current_value = query_result['result']['value']
              if current_value != value:
                  update_result = __salt__['jboss7.update_simple_binding'](binding_name=key, value=value, jboss_config=jboss_config)
                  if update_result['success']:
                      has_changed = True
                      __log_binding_change(ret['changes'], 'changed', key, value, current_value)
                  else:
                      raise CommandExecutionError(update_result['failure-description'])
          else:
              if query_result['err_code'] == 'JBAS014807':  # ok, resource not exists:
                  create_result = __salt__['jboss7.create_simple_binding'](binding_name=key, value=value, jboss_config=jboss_config)
                  if create_result['success']:
                      has_changed = True
                      __log_binding_change(ret['changes'], 'added', key, value)
                  else:
                      raise CommandExecutionError(create_result['failure-description'])
              else:
                  raise CommandExecutionError(query_result['failure-description'])
  
      if has_changed:
          ret['comment'] = 'Bindings changed.'
      return ret
  
  
  def __log_binding_change(changes, type_, key, new, old=None):
      if type_ not in changes:
          changes[type_] = ''
      if old is None:
          changes[type_] += key + ':' + new + '\n'
      else:
          changes[type_] += key + ':' + old + '->' + new + '\n'
  
  
  def deployed(name, jboss_config, artifact=None, salt_source=None):
      '''
      Ensures that the given application is deployed on server.
  
      jboss_config:
          Dict with connection properties (see state description)
      artifact:
          If set, the artifact will be fetched from artifactory. This is a Dict object with the following properties:
             - artifactory_url: Full url to artifactory instance, for example: http://artifactory.intranet.company.com/artifactory
             - repository: One of the repositories, for example: libs-snapshots, ext-release-local, etc..
             - artifact_id: Artifact ID of the artifact
             - group_id: Group ID of the artifact
             - packaging: war/jar/ear, etc...
             - version: Artifact version. If latest_snapshot is set to True, the value of this attribute will be ignored, and newest snapshot will be taken instead.
             - latest_snapshot: If set to True and repository is a snapshot repository it will automatically select the newest snapshot.
             - snapshot_version: Exact version of the snapshot (with timestamp). A snapshot version may have several builds and a way to differentiate is to provide a build timestamp.
             - target_dir: Temporary directory on minion where artifacts will be downloaded
      salt_source:
          If set, the artifact to be deployed will be fetched from salt master. This is a Dict object with the following properties:
             - source: File on salt master (eg. salt://application-web-0.39.war)
             - target_file: Temporary file on minion to save file to (eg. '/tmp/application-web-0.39.war')
             - undeploy: Regular expression to match against existing deployments. If any deployment matches the regular expression then it will be undeployed.
  
      The deployment consists of the following steps:
  
      * Fetch artifact (salt filesystem, artifact or filesystem on minion)
      * Check if same artifact is not deployed yet (perhaps with different version)
      * Undeploy the artifact if it is already deployed
      * Deploy the new artifact
  
      Examples:
  
      Deployment of a file from Salt file system:
  
      .. code-block:: yaml
  
          application_deployed:
            jboss7.deployed:
             - salt_source:
                  source: salt://application-web-0.39.war
                  target_file: '/tmp/application-web-0.39.war'
                  undeploy: 'application-web-.*'
             - jboss_config: {{ pillar['jboss'] }}
  
      Here, application-web-0.39.war file is downloaded from Salt file system to /tmp/application-web-0.39.war file on minion.
      Existing deployments are checked if any of them matches 'application-web-.*' regular expression, and if so then it
      is undeployed before deploying the application. This is useful to automate deployment of new application versions.
  
      JBoss state is capable of deploying artifacts directly from Artifactory repository. Here are some examples of deployments:
  
      1) Deployment of released version of artifact from Artifactory.
  
      .. code-block:: yaml
  
              application_deployed:
                jboss7.deployed:
                 - artifact:
                     artifactory_url: http://artifactory.intranet.company.com/artifactory
                     repository: 'ext-release-local'
                     artifact_id: 'webcomponent'
                     group_id: 'com.company.application'
                     packaging: 'war'
                     version: '0.1'
                     target_dir: '/tmp'
                  - jboss_config: {{ pillar['jboss'] }}
  
      This performs the following operations:
  
      * Download artifact from artifactory. In the example above the artifact will be fetched from: http://artifactory.intranet.company.com/artifactory/ext-release-local/com/company/application/webcomponent/0.1/webcomponent-0.1.war
        As a rule, for released versions the artifacts are downloaded from: artifactory_url/repository/group_id_with_slashed_instead_of_dots/artifact_id/version/artifact_id-version.packaging'
        This follows artifactory convention for artifact resolution. By default the artifact will be downloaded to /tmp directory on minion.
      * Connect to JBoss via controller (defined in jboss_config dict) and check if the artifact is not deployed already. In case of artifactory
        it will check if any deployment's name starts with artifact_id value. If deployment already exists it will be undeployed
      * Deploy the downloaded artifact to JBoss via cli interface.
  
      2) Deployment of last updated version of given SNAPSHOT version of artifact from Artifactory.
  
      .. code-block:: yaml
  
          application_deployed:
            jboss7.deployed:
             - artifact:
                 artifactory_url: http://artifactory.intranet.company.com/artifactory
                 repository: 'ext-snapshot-local'
                 artifact_id: 'webcomponent'
                 group_id: 'com.company.application'
                 packaging: 'war'
                 version: '0.1-SNAPSHOT'
              - jboss_config: {{ pillar['jboss'] }}
  
      Deploying snapshot version involves an additional step of resolving the exact version of the artifact (including the timestamp), which
      is not necessary when deploying a release.
      In the example above first a request will be made to retrieve the update timestamp from:
      http://artifactory.intranet.company.com/artifactory/ext-snapshot-local/com/company/application/webcomponent/0.1-SNAPSHOT/maven-metadata.xml
      Then the artifact will be fetched from
      http://artifactory.intranet.company.com/artifactory/ext-snapshot-local/com/company/application/webcomponent/0.1-SNAPSHOT/webcomponent-RESOLVED_SNAPSHOT_VERSION.war
  
      .. note:: In order to perform a snapshot deployment you have to:
  
          * Set repository to a snapshot repository.
          * Choose a version that ends with "SNAPSHOT" string.
            Snapshot repositories have a different layout and provide some extra information that is needed for deployment of the last or a specific snapshot.
  
      3) Deployment of SNAPSHOT version (with exact timestamp) of artifact from Artifactory.
  
      If you need to deploy an exact version of the snapshot you may provide snapshot_version parameter.
  
      .. code-block:: yaml
  
          application_deployed:
            jboss7.deployed:
             - artifact:
                 artifactory_url: http://artifactory.intranet.company.com/artifactory
                 repository: 'ext-snapshot-local'
                 artifact_id: 'webcomponent'
                 group_id: 'com.company.application'
                 packaging: 'war'
                 version: '0.1-SNAPSHOT'
                 snapshot_version: '0.1-20141023.131756-19'
              - jboss_config: {{ pillar['jboss'] }}
  
  
      In this example the artifact will be retrieved from:
      http://artifactory.intranet.company.com/artifactory/ext-snapshot-local/com/company/application/webcomponent/0.1-SNAPSHOT/webcomponent-0.1-20141023.131756-19.war
  
      4) Deployment of latest snapshot of artifact from Artifactory.
  
      .. code-block:: yaml
  
          application_deployed:
            jboss7.deployed:
             - artifact:
                 artifactory_url: http://artifactory.intranet.company.com/artifactory
                 repository: 'ext-snapshot-local'
                 artifact_id: 'webcomponent'
                 group_id: 'com.company.application'
                 packaging: 'war'
                 latest_snapshot: True
              - jboss_config: {{ pillar['jboss'] }}
  
      Instead of providing an exact version of a snapshot it is sometimes more convenient to get the newest version. If artifact.latest_snapshot
      is set to True, then the newest snapshot will be downloaded from Artifactory. In this case it is not necessary to specify version.
      This is particulary useful when integrating with CI tools that will deploy the current snapshot to the Artifactory.
  
      '''
      log.debug(" ======================== STATE: jboss7.deployed (name: %s) ", name)
      ret = {'name': name,
             'result': True,
             'changes': {},
             'comment': ''}
  
      comment = ''
  
      validate_success, validate_comment = __validate_arguments(jboss_config, artifact, salt_source)
      if not validate_success:
          return _error(ret, validate_comment)
  
      log.debug('artifact=%s', str(artifact))
      resolved_source, get_artifact_comment = __get_artifact(artifact, salt_source)
      log.debug('resolved_source=%s', resolved_source)
      log.debug('get_artifact_comment=%s', get_artifact_comment)
  
      comment = __append_comment(new_comment=get_artifact_comment, current_comment=comment)
      if resolved_source is None:
          return _error(ret, get_artifact_comment)
  
      find_success, deployment, find_comment = __find_deployment(jboss_config, artifact, salt_source)
      if not find_success:
          return _error(ret, find_comment)
  
      log.debug('deployment=%s', deployment)
      if deployment is not None:
          __salt__['jboss7.undeploy'](jboss_config, deployment)
          ret['changes']['undeployed'] = deployment
  
      deploy_result = __salt__['jboss7.deploy'](jboss_config=jboss_config, source_file=resolved_source)
      log.debug('deploy_result=%s', str(deploy_result))
      if deploy_result['success']:
          comment = __append_comment(new_comment='Deployment completed.', current_comment=comment)
          ret['comment'] = comment
          ret['changes']['deployed'] = resolved_source
      else:
          comment = __append_comment(new_comment='''Deployment failed\nreturn code={retcode}\nstdout='{stdout}'\nstderr='{stderr}'''.format(**deploy_result), current_comment=comment)
          return _error(ret, comment)
  
      return ret
  
  
  def __validate_arguments(jboss_config, artifact, salt_source):
      result, comment = __check_dict_contains(jboss_config, 'jboss_config', ['cli_path', 'controller'])
      if artifact is None and salt_source is None:
          result = False
          comment = __append_comment('No salt_source or artifact defined', comment)
      if artifact:
          result, comment = __check_dict_contains(artifact, 'artifact', ['artifactory_url', 'repository', 'artifact_id', 'group_id', 'packaging'], comment, result)
          if 'latest_snapshot' in artifact and isinstance(artifact['latest_snapshot'], str):
              if artifact['latest_snapshot'] == 'True':
                  artifact['latest_snapshot'] = True
              elif artifact['latest_snapshot'] == 'False':
                  artifact['latest_snapshot'] = False
              else:
                  result = False
                  comment = __append_comment('Cannot convert jboss_config.latest_snapshot={0} to boolean'.format(artifact['latest_snapshot']), comment)
          if 'version' not in artifact and ('latest_snapshot' not in artifact or not artifact['latest_snapshot']):
              result = False
              comment = __append_comment('No version or latest_snapshot=True in artifact')
      if salt_source:
          result, comment = __check_dict_contains(salt_source, 'salt_source', ['source', 'target_file'], comment, result)
  
      return result, comment
  
  
  def __find_deployment(jboss_config, artifact=None, salt_source=None):
      result = None
      success = True
      comment = ''
      deployments = __salt__['jboss7.list_deployments'](jboss_config)
      if artifact is not None:
          for deployment in deployments:
              if deployment.startswith(artifact['artifact_id']):
                  if result is not None:
                      success = False
                      comment = "More than one deployment's name starts with {0}. \n" \
                                "For deployments from artifactory existing deployments on JBoss are searched to find one that starts with artifact_id.\n"\
                                "Existing deployments: {1}".format(artifact['artifact_id'], ",".join(deployments))
                  else:
                      result = deployment
      elif salt_source is not None and salt_source['undeploy']:
          deployment_re = re.compile(salt_source['undeploy'])
          for deployment in deployments:
              if deployment_re.match(deployment):
                  if result is not None:
                      success = False
                      comment = "More than one deployment matches regular expression: {0}. \n" \
                                "For deployments from Salt file system deployments on JBoss are searched to find one that matches regular expression in 'undeploy' parameter.\n" \
                                "Existing deployments: {1}".format(salt_source['undeploy'], ",".join(deployments))
                  else:
                      result = deployment
  
      return success, result, comment
  
  
  def __get_artifact(artifact, salt_source):
      resolved_source = None
      comment = None
  
      if artifact is None and salt_source is None:
          log.debug('artifact == None and salt_source == None')
          comment = 'No salt_source or artifact defined'
      elif isinstance(artifact, dict):
          log.debug('artifact from artifactory')
          try:
              fetch_result = __fetch_from_artifactory(artifact)
              log.debug('fetch_result={0}'.format(fetch_result))
          except Exception as exception:
              log.debug(traceback.format_exc())
              return None, exception
  
          if fetch_result['status']:
              resolved_source = fetch_result['target_file']
              comment = fetch_result['comment']
          else:
              comment = 'Cannot fetch artifact (artifactory comment:{0}) '.format(fetch_result['comment'])
      elif isinstance(salt_source, dict):
          log.debug('file from salt master')
  
          try:
              sfn, source_sum, comment_ = __salt__['file.get_managed'](
                  name=salt_source['target_file'],
                  template=None,
                  source=salt_source['source'],
                  source_hash=None,
                  user=None,
                  group=None,
                  mode=None,
                  saltenv=__env__,
                  context=None,
                  defaults=None,
                  kwargs=None)
  
              manage_result = __salt__['file.manage_file'](
                  name=salt_source['target_file'],
                  sfn=sfn,
                  ret=None,
                  source=salt_source['source'],
                  source_sum=source_sum,
                  user=None,
                  group=None,
                  mode=None,
                  saltenv=__env__,
                  backup=None,
                  makedirs=False,
                  template=None,
                  show_diff=True,
                  contents=None,
                  dir_mode=None)
              if manage_result['result']:
                  resolved_source = salt_source['target_file']
              else:
                  comment = manage_result['comment']
  
          except Exception as e:
              log.debug(traceback.format_exc())
              comment = 'Unable to manage file: {0}'.format(e)
  
      return resolved_source, comment
  
  
  def __fetch_from_artifactory(artifact):
      target_dir = '/tmp'
      if 'temp_dir' in artifact:
          target_dir = artifact['temp_dir']
  
      if 'latest_snapshot' in artifact and artifact['latest_snapshot']:
          fetch_result = __salt__['artifactory.get_latest_snapshot'](artifactory_url=artifact['artifactory_url'],
                                                                     repository=artifact['repository'],
                                                                     group_id=artifact['group_id'],
                                                                     artifact_id=artifact['artifact_id'],
                                                                     packaging=artifact['packaging'],
                                                                     target_dir=target_dir)
      elif str(artifact['version']).endswith('SNAPSHOT'):
          if 'snapshot_version' in artifact:
              fetch_result = __salt__['artifactory.get_snapshot'](artifactory_url=artifact['artifactory_url'],
                                                                  repository=artifact['repository'],
                                                                  group_id=artifact['group_id'],
                                                                  artifact_id=artifact['artifact_id'],
                                                                  packaging=artifact['packaging'],
                                                                  version=artifact['version'],
                                                                  snapshot_version=artifact['snapshot_version'],
                                                                  target_dir=target_dir)
          else:
              fetch_result = __salt__['artifactory.get_snapshot'](artifactory_url=artifact['artifactory_url'],
                                                                  repository=artifact['repository'],
                                                                  group_id=artifact['group_id'],
                                                                  artifact_id=artifact['artifact_id'],
                                                                  packaging=artifact['packaging'],
                                                                  version=artifact['version'],
                                                                  target_dir=target_dir)
      else:
          fetch_result = __salt__['artifactory.get_release'](artifactory_url=artifact['artifactory_url'],
                                                             repository=artifact['repository'],
                                                             group_id=artifact['group_id'],
                                                             artifact_id=artifact['artifact_id'],
                                                             packaging=artifact['packaging'],
                                                             version=artifact['version'],
                                                             target_dir=target_dir)
      return fetch_result
  
  
  def reloaded(name, jboss_config, timeout=60, interval=5):
      '''
      Reloads configuration of jboss server.
  
      jboss_config:
          Dict with connection properties (see state description)
      timeout:
          Time to wait until jboss is back in running state. Default timeout is 60s.
      interval:
          Interval between state checks. Default interval is 5s. Decreasing the interval may slightly decrease waiting time
          but be aware that every status check is a call to jboss-cli which is a java process. If interval is smaller than
          process cleanup time it may easily lead to excessive resource consumption.
  
      This step performs the following operations:
  
      * Ensures that server is in running or reload-required state (by reading server-state attribute)
      * Reloads configuration
      * Waits for server to reload and be in running state
  
      Example:
  
      .. code-block:: yaml
  
          configuration_reloaded:
             jboss7.reloaded:
              - jboss_config: {{ pillar['jboss'] }}
      '''
      log.debug(" ======================== STATE: jboss7.reloaded (name: %s) ", name)
      ret = {'name': name,
             'result': True,
             'changes': {},
             'comment': ''}
  
      status = __salt__['jboss7.status'](jboss_config)
      if not status['success'] or status['result'] not in ('running', 'reload-required'):
          ret['result'] = False
          ret['comment'] = "Cannot reload server configuration, it should be up and in 'running' or 'reload-required' state."
          return ret
  
      result = __salt__['jboss7.reload'](jboss_config)
      if result['success'] or \
                      'Operation failed: Channel closed' in result['stdout'] or \
                      'Communication error: java.util.concurrent.ExecutionException: Operation failed' in result['stdout']:
          wait_time = 0
          status = None
          while (status is None or not status['success'] or status['result'] != 'running') and wait_time < timeout:
              time.sleep(interval)
              wait_time += interval
              status = __salt__['jboss7.status'](jboss_config)
  
          if status['success'] and status['result'] == 'running':
              ret['result'] = True
              ret['comment'] = 'Configuration reloaded'
              ret['changes']['reloaded'] = 'configuration'
          else:
              ret['result'] = False
              ret['comment'] = 'Could not reload the configuration. Timeout ({0} s) exceeded. '.format(timeout)
              if not status['success']:
                  ret['comment'] = __append_comment('Could not connect to JBoss controller.', ret['comment'])
              else:
                  ret['comment'] = __append_comment(('Server is in {0} state'.format(status['result'])), ret['comment'])
      else:
          ret['result'] = False
          ret['comment'] = 'Could not reload the configuration, stdout:'+result['stdout']
  
      return ret
  
  
  def __check_dict_contains(dct, dict_name, keys, comment='', result=True):
      for key in keys:
          if key not in six.iterkeys(dct):
              result = False
              comment = __append_comment("Missing {0} in {1}".format(key, dict_name), comment)
      return result, comment
  
  
  def __append_comment(new_comment, current_comment=''):
      return current_comment+'\n'+new_comment
  
  
  def _error(ret, err_msg):
      ret['result'] = False
      ret['comment'] = err_msg
      return ret
  