#
# Cookbook:: openstack-common
# library:: cli
#
# Copyright:: 2014-2021, 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.
#

require 'chef/mixin/shell_out'
include Chef::Mixin::ShellOut
require 'uri'

# CLI methods
module ::Openstack
  # return an environment suitable for calling openstack commands.
  #
  # @param [String] user name
  # @param [String] tenant name
  # @return [Hash] environment
  def openstack_command_env(name, project, user_domain, project_domain)
    identity_endpoint = public_endpoint 'identity'
    auth_url = identity_endpoint.to_s

    pass = get_password 'user', name
    {
      'OS_USERNAME' => name,
      'OS_PASSWORD' => pass,
      'OS_PROJECT_NAME' => project,
      'OS_USER_DOMAIN_NAME' => user_domain,
      'OS_PROJECT_DOMAIN_NAME' => project_domain,
      'OS_IDENTITY_API_VERSION' => '3',
      'OS_AUTH_URL' => auth_url,
    }
  end

  # return stdout from calling an openstack command.
  #
  # @param [String] command to run
  # @param [String] command options
  # @param [Hash] environment to use
  # @param [Hash] optional command argument/values pairs
  # @return [String] stdout or fail
  #
  def openstack_command(cmd, options = '', env = {}, args = {})
    # NOTE: Here we split options (which creates an array) and then merge that
    #       array into [cmd].  This is done to accomdate cmd + options like:
    #       keystone user-list
    #       glance   image-show <id|name>
    openstackcmd = [cmd]
    args.each do |key, val|
      openstackcmd << "--#{key}"
      openstackcmd << val.to_s unless val.to_s.empty?
    end
    # If options is a string, split on whitespace into array; otherwise, assume
    # it is an array already and leave it untouched.
    options = options.split if options.instance_of? String
    openstackcmd = openstackcmd.concat(options)
    Chef::Log.debug("Running openstack command: #{openstackcmd} with environment: #{env}")
    result = shell_out(openstackcmd, env: env)
    Chef::Log.debug("Output for command: #{cmd}:\n#{result.stdout}\n#{result.stderr}")
    raise "#{result.stderr} (#{result.exitstatus})" if result.exitstatus != 0
    result.stdout
  end

  # return uuid for a resource.
  #
  # @param [String] client of resource (keystone, neutron, glance, ...)
  # @param [String] type of resource (user, service, tenant, endpoint, role; net, subnet, router, ...)
  # @param [String] key of resource to match
  # @param [String] value of resource key to match
  # @param [Hash] environment to use.
  # @param [Hash] optional command argument/values pairs
  # @param [String] optional uuid field to match
  # @return [String] uuid or nil
  #
  def get_uuid(client, type, key, value, env, args = {}, uuid_field = 'id') # rubocop: disable Metrics/ParameterLists
    begin
      output = openstack_command(client, "#{type} list", env, args)
      prettytable_to_array(output).each do |obj|
        return obj[uuid_field] if obj.key?(uuid_field) && obj[key] == value
      end
    rescue RuntimeError => e
      raise "Could not lookup uuid for #{type}:#{key}=>#{value}. Error was #{e.message}"
    end
    nil
  end

  # return uuid for an identity resource.
  #
  # @param [String] type of resource (user, service, tenant, endpoint, role)
  # @param [String] key of resource to match
  # @param [String] value of resource key to match
  # @param [Hash] environment to use.
  # @param [Hash] optional command argument/values pairs
  # @param [String] optional uuid field to match
  # @return [String] uuid or nil
  #
  # TODO: update openstack-identity register provider to use these functions.
  #
  def identity_uuid(*args)
    get_uuid('openstack', *args)
  end

  # return id for a glance image.
  #
  # @param [String] name of image
  # @param [Hash] environment to use.
  # @param [Hash] optional command argument/values pairs
  # @return [String] id or nil
  def image_id(name, env, args = {})
    get_uuid('openstack', 'image', 'Name', name, env, args, 'ID')
  end

  # return uuid for a network resource.
  #
  # @param [String] type of resource (net, subnet, router, port, ...)
  # @param [String] key of resource to match
  # @param [String] value of resource key to match
  # @param [Hash] environment to use.
  # @param [Hash] optional command argument/values pairs
  # @param [String] optional uuid field to match
  # @return [String] uuid or nil
  #
  def network_uuid(*args)
    get_uuid('openstack', *args)
  end
end