envoi-mam-agent 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +8 -1
- data/docs/restore.md +43 -0
- data/envoi-mam-agent.gemspec +11 -4
- data/lib/cantemo/portal/agent/version.rb +7 -0
- data/lib/cantemo/portal/api/client.rb +82 -0
- data/lib/envoi/aspera/watch_service/client.rb +397 -0
- data/lib/envoi/aspera/watch_service/watch_folder.rb +322 -0
- data/lib/envoi/mam/agent.rb +34 -14
- data/lib/envoi/mam/agent/cli.rb +3 -3
- data/lib/envoi/mam/agent/cli/commands.rb +26 -16
- data/lib/envoi/mam/agent/cli/commands/iconik.rb +1 -1
- data/lib/envoi/mam/agent/cli/commands/mediasilo.rb +1 -1
- data/lib/envoi/mam/agent/cli/commands/restore.rb +52 -0
- data/lib/envoi/mam/agent/cli/commands/vidispine.rb +1 -1
- data/lib/envoi/mam/agent/cli/commands/wiredrive.rb +1 -1
- data/lib/envoi/mam/agent/transfer_client/aspera.rb +140 -18
- data/lib/envoi/mam/agent/transfer_client/s3.rb +13 -5
- data/lib/envoi/mam/agent/version.rb +1 -1
- data/lib/envoi/mam/cantemo/agent.rb +353 -0
- data/lib/envoi/mam/vidispine/agent.rb +7 -2
- data/lib/envoi/restore/agent.rb +73 -0
- data/lib/envoi/restore/glacier-restore-event-handler.rb +94 -0
- data/lib/envoi/restore/sqs-queue-worker.rb +68 -0
- metadata +50 -14
- data/.rbenv-gemsets +0 -3
- data/.ruby-version +0 -1
@@ -10,7 +10,7 @@ require 'envoi/mam/iconik/agent'
|
|
10
10
|
current_command = ARGV.shift
|
11
11
|
ARGV << '--help' if ARGV.empty?
|
12
12
|
|
13
|
-
default_config_file_paths = Envoi::Mam::Agent::
|
13
|
+
default_config_file_paths = Envoi::Mam::Agent::CLI::CONFIG_FILE_PATHS
|
14
14
|
|
15
15
|
aspera_ascp_paths = Envoi::Mam::Agent::TransferClient::Aspera::ASCP_PATHS
|
16
16
|
default_aspera_ascp_path = aspera_ascp_paths.find { |v| File.exist? v } || aspera_ascp_paths.first
|
@@ -11,7 +11,7 @@ require 'envoi/mam/mediasilo/agent'
|
|
11
11
|
current_command = ARGV.shift
|
12
12
|
ARGV << '--help' if ARGV.empty?
|
13
13
|
|
14
|
-
default_config_file_paths = Envoi::Mam::Agent::
|
14
|
+
default_config_file_paths = Envoi::Mam::Agent::CLI::CONFIG_FILE_PATHS
|
15
15
|
|
16
16
|
aspera_ascp_paths = Envoi::Mam::Agent::TransferClient::Aspera::ASCP_PATHS
|
17
17
|
default_aspera_ascp_path = aspera_ascp_paths.find { |v| File.exist? v } || aspera_ascp_paths.first
|
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
lib_path = __FILE__ == '(irb)' ? File.join(Dir.cwd, 'lib') : File.expand_path('../../../lib', __FILE__)
|
3
|
+
$:.unshift(lib_path) unless $:.include?(lib_path) or !File.exists?(lib_path)
|
4
|
+
require 'rubygems'
|
5
|
+
require 'optparse'
|
6
|
+
require 'open3'
|
7
|
+
|
8
|
+
require 'envoi/mam/agent/cli'
|
9
|
+
require 'envoi/restore/agent'
|
10
|
+
|
11
|
+
default_config_file_paths = Envoi::Mam::Agent::CLI::CONFIG_FILE_PATHS
|
12
|
+
|
13
|
+
aspera_ascp_paths = Envoi::Mam::Agent::TransferClient::Aspera::ASCP_PATHS
|
14
|
+
default_aspera_ascp_path = aspera_ascp_paths.find { |v| File.exist? v } || aspera_ascp_paths.first
|
15
|
+
|
16
|
+
current_command = ARGV.shift
|
17
|
+
args = {
|
18
|
+
:config_file_path => default_config_file_paths,
|
19
|
+
:default_ascp_path => default_aspera_ascp_path,
|
20
|
+
:dry_run => false,
|
21
|
+
:operation => :download,
|
22
|
+
:preserve_path => true,
|
23
|
+
:transfer_type => '',
|
24
|
+
}
|
25
|
+
|
26
|
+
args[:config_service_app_id] = ENV['ENVOI_MAM_AGENT_APP_ID']
|
27
|
+
args[:config_service_app_token] = ENV['ENVOI_MAM_AGENT_APP_TOKEN']
|
28
|
+
args[:config_service_app_url] = ENV['ENVOI_MAM_AGENT_APP_URL']
|
29
|
+
|
30
|
+
op = OptionParser.new
|
31
|
+
op.on('-c', '--config-file-path FILEPATH', 'The path to the configuration file.',
|
32
|
+
"Default Path(s): #{args[:config_file_path]}") { |v| args[:config_file_path] = v }
|
33
|
+
# op.on('--agent-app-id ID', '') { |v| args[:config_service_app_id] = v }
|
34
|
+
# op.on('--agent-token TOKEN', '') { |v| args[:config_service_app_token] = v }
|
35
|
+
# op.on('--agent-service-api URL', '') { |v| args[:config_service_api_url] = v }
|
36
|
+
|
37
|
+
op.on('-h', '--help', 'Show this message.') { puts op; exit }
|
38
|
+
op.load
|
39
|
+
op.parse!
|
40
|
+
|
41
|
+
begin
|
42
|
+
agent = Envoi::Restore::Agent.load_config_and_init(args)
|
43
|
+
rescue => e
|
44
|
+
abort(e.message)
|
45
|
+
end
|
46
|
+
|
47
|
+
begin
|
48
|
+
agent.run
|
49
|
+
rescue Interrupt
|
50
|
+
agent.stop
|
51
|
+
end
|
52
|
+
|
@@ -8,7 +8,7 @@ require 'open3'
|
|
8
8
|
require 'envoi/mam/agent/cli'
|
9
9
|
require 'envoi/mam/vidispine/agent'
|
10
10
|
|
11
|
-
default_config_file_paths = Envoi::Mam::Agent::
|
11
|
+
default_config_file_paths = Envoi::Mam::Agent::CLI::CONFIG_FILE_PATHS
|
12
12
|
|
13
13
|
aspera_ascp_paths = Envoi::Mam::Agent::TransferClient::Aspera::ASCP_PATHS
|
14
14
|
default_aspera_ascp_path = aspera_ascp_paths.find { |v| File.exist? v } || aspera_ascp_paths.first
|
@@ -11,7 +11,7 @@ require 'envoi/mam/iconik/agent'
|
|
11
11
|
current_command = ARGV.shift
|
12
12
|
ARGV << '--help' if ARGV.empty?
|
13
13
|
|
14
|
-
default_config_file_paths = Envoi::Mam::Agent::
|
14
|
+
default_config_file_paths = Envoi::Mam::Agent::CLI::CONFIG_FILE_PATHS
|
15
15
|
# test_args = %w(--download --send-to-envoi --destination-path /tmp)
|
16
16
|
# ARGV.concat test_args
|
17
17
|
args = {
|
@@ -1,4 +1,12 @@
|
|
1
1
|
require 'envoi/mam/agent/transfer_client'
|
2
|
+
require 'asperalm/fasp/local'
|
3
|
+
|
4
|
+
module Asperalm
|
5
|
+
class Log
|
6
|
+
def logger=(new_logger) @logger = new_logger end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
2
10
|
|
3
11
|
module Envoi
|
4
12
|
module Mam
|
@@ -11,12 +19,12 @@ module Envoi
|
|
11
19
|
DEFAULT_ASCP_ARGS = '-v -k3 --overwrite=diff -P 33001'
|
12
20
|
|
13
21
|
ASCP_PATHS = [
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
22
|
+
'/usr/local/bin/ascp',
|
23
|
+
'/usr/bin/ascp',
|
24
|
+
'/Library/Aspera/bin/ascp',
|
25
|
+
'~/Applications/Aspera CLI/bin/ascp',
|
26
|
+
'~/Applications/Aspera Connect.app/Contents/Resources/ascp',
|
27
|
+
'/Applications/Aspera Connect.app/Contents/Resources/ascp'
|
20
28
|
]
|
21
29
|
ASCP_PATHS.map { |v| File.expand_path(v) }
|
22
30
|
|
@@ -32,52 +40,166 @@ module Envoi
|
|
32
40
|
File.executable?(@ascp_path)
|
33
41
|
end
|
34
42
|
|
35
|
-
def
|
43
|
+
def build_asperala_transfer_args(config, mode, source_path, target_path)
|
44
|
+
|
36
45
|
aspera_host_address = config['host_address'] || config['host']
|
37
46
|
aspera_username = config['username']
|
38
47
|
aspera_password = config['password']
|
48
|
+
aspera_ssh_port = config['ssh_port']
|
49
|
+
|
50
|
+
aspera_url = config['url'] || config['uri']
|
51
|
+
if aspera_url
|
52
|
+
aspera_uri = URI(aspera_url)
|
53
|
+
aspera_host_address ||= aspera_uri.host if aspera_uri
|
54
|
+
un, pw = (aspera_uri.userinfo || '').split(':')
|
55
|
+
aspera_username ||= un if un
|
56
|
+
aspera_password ||= pw if pw
|
57
|
+
aspera_ssh_port = aspera_uri.port
|
58
|
+
end
|
59
|
+
|
60
|
+
force_username_password = config.fetch('force_username_password', false)
|
61
|
+
|
39
62
|
aspera_token = config['aspera_transfer_token'] || config['aspera_token'] || config['token'] || config['transfer_token']
|
63
|
+
if aspera_username && aspera_password && !force_username_password && (aspera_token.nil? || aspera_token.empty?)
|
64
|
+
aspera_token = %(Basic #{["#{aspera_username}:#{aspera_password}"]
|
65
|
+
.pack('m')
|
66
|
+
.delete("\r\n")})
|
67
|
+
aspera_username = 'xfer'
|
68
|
+
aspera_password = nil
|
69
|
+
end
|
70
|
+
|
71
|
+
# @ascp_path = config['ascp_path'] || default_ascp_path
|
72
|
+
# @ascp_path = File.expand_path(@ascp_path)
|
73
|
+
# aspera_ascp_path = @ascp_path
|
74
|
+
|
75
|
+
env_vars = { }
|
76
|
+
env_vars['ASPERA_SCP_PASS'] = aspera_password if aspera_password
|
77
|
+
# env_vars['ASPERA_SCP_TOKEN'] = aspera_token if aspera_token
|
78
|
+
|
79
|
+
ascp_args = config['ascp_args'] ||
|
80
|
+
default_ascp_args ||
|
81
|
+
(agent.respond_to?(:default_ascp_args) ? agent.default_ascp_args : '')
|
82
|
+
|
83
|
+
tags = config['tags'] ||= { }
|
84
|
+
aspera_tags = tags['aspera'] ||= { }
|
85
|
+
aspera_tags['xfer_id'] ||= SecureRandom.uuid
|
86
|
+
|
87
|
+
cmdline_args = [
|
88
|
+
'--mode', mode,
|
89
|
+
'--host', aspera_host_address,
|
90
|
+
'--user', aspera_username,
|
91
|
+
'--tags64', Base64.strict_encode64(JSON.generate(tags)),
|
92
|
+
]
|
93
|
+
cmdline_args.concat [ '-P', aspera_ssh_port ] if aspera_ssh_port
|
94
|
+
cmdline_args.concat ascp_args.is_a?(Array) ? ascp_args : ascp_args.split(' ') if ascp_args && !ascp_args.empty?
|
95
|
+
if aspera_token && !aspera_token.empty?
|
96
|
+
cmdline_args.concat ['-W', aspera_token, '-i', 'asperaweb_id_dsa.openssh' ]
|
97
|
+
end
|
98
|
+
cmdline_args.concat [ source_path.gsub('"', '\"'), target_path.gsub('"', '\"') ]
|
99
|
+
|
100
|
+
{ env: env_vars, args: cmdline_args, ascp_version: :ascp }
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_ascp_command(config, mode, source_path, target_path)
|
104
|
+
aspera_host_address = config['host_address'] || config['host']
|
105
|
+
aspera_username = config['username']
|
106
|
+
aspera_password = config['password']
|
107
|
+
aspera_ssh_port = config['ssh_port']
|
108
|
+
|
109
|
+
aspera_url = config['url'] || config['uri']
|
110
|
+
if aspera_url
|
111
|
+
aspera_uri = URI(aspera_url)
|
112
|
+
aspera_host_address ||= aspera_uri.host if aspera_uri
|
113
|
+
un, pw = (aspera_uri.userinfo || '').split(':')
|
114
|
+
aspera_username ||= un if un
|
115
|
+
aspera_password ||= pw if pw
|
116
|
+
aspera_ssh_port ||= aspera_uri.port
|
117
|
+
end
|
40
118
|
|
119
|
+
aspera_token = config['aspera_transfer_token'] || config['aspera_token'] || config['token'] || config['transfer_token']
|
120
|
+
if aspera_username && aspera_password && (aspera_token.nil? || aspera_token.empty?)
|
121
|
+
aspera_token = %(Basic #{["#{aspera_username}:#{aspera_password}"]
|
122
|
+
.pack('m')
|
123
|
+
.delete("\r\n")})
|
124
|
+
aspera_username = 'xfer'
|
125
|
+
end
|
41
126
|
@ascp_path = config['ascp_path'] || default_ascp_path
|
42
127
|
@ascp_path = File.expand_path(@ascp_path)
|
43
128
|
|
44
129
|
aspera_ascp_path = @ascp_path
|
45
130
|
|
131
|
+
tags = config['tags'] || { }
|
132
|
+
aspera_tags = tags['aspera'] || { }
|
133
|
+
aspera_tags['xfer_id'] ||= SecureRandom.uuid
|
134
|
+
|
135
|
+
|
46
136
|
ascp_args = config['ascp_args'] || default_ascp_args || agent.default_ascp_args
|
137
|
+
|
47
138
|
# command = %(export ASPERA_SCP_PASS="#{aspera_password}"; "#{aspera_ascp_path}" #{ascp_args} --host="#{aspera_host_address}" --user=#{aspera_username} --mode=#{mode} "#{source_path}" "#{target_path}" )
|
48
139
|
command = ''
|
49
140
|
command << %(export ASPERA_SCP_PASS="#{aspera_password}"; ) if aspera_password
|
50
141
|
command << %("#{aspera_ascp_path}" --mode=#{mode} --host="#{aspera_host_address}" --user="#{aspera_username}")
|
51
|
-
command << %( #{
|
142
|
+
command << %( -P #{aspera_ssh_port}) if aspera_ssh_port
|
143
|
+
command << %(--tags64 #{Base64.strict_encode64(JSON.generate(tags))}) if tags && !tags.empty?
|
144
|
+
if ascp_args && !ascp_args.empty?
|
145
|
+
command << (ascp_args.is_a?(Array)) ? ascp_args.join(' ') : ascp_args
|
146
|
+
end
|
52
147
|
command << %( -W "#{aspera_token}") if aspera_token && !aspera_token.empty?
|
53
148
|
command << %( "#{source_path.gsub('"', '\"')}" "#{target_path.gsub('"', '\"')}")
|
54
149
|
end
|
55
150
|
|
56
151
|
def download(config, path, destination_path = DEFAULT_DESTINATION_PATH)
|
57
152
|
aspera_base_path = config['base_path'] || ''
|
58
|
-
source_path = File.join(aspera_base_path, path)
|
59
|
-
command = build_ascp_command(config, 'recv', source_path, destination_path)
|
153
|
+
source_path = aspera_base_path.empty? ? path : File.join(aspera_base_path, path)
|
60
154
|
|
61
|
-
|
62
|
-
|
63
|
-
return false
|
64
|
-
end
|
65
|
-
agent.shell_execute(command)
|
155
|
+
mode = 'recv'
|
156
|
+
transfer(config, mode, source_path, destination_path)
|
66
157
|
end
|
67
158
|
|
68
159
|
def upload(config, path, destination_path = DEFAULT_DESTINATION_PATH)
|
69
160
|
aspera_base_path = config['base_path'] || ''
|
70
161
|
source_path = path
|
71
162
|
destination_path = aspera_base_path.empty? ? destination_path : File.join(aspera_base_path, destination_path)
|
72
|
-
|
163
|
+
|
164
|
+
mode = 'send'
|
165
|
+
transfer(config, mode, source_path, destination_path)
|
166
|
+
end
|
167
|
+
|
168
|
+
def transfer(config, mode, source_path, destination_path)
|
169
|
+
if true
|
170
|
+
transfer_using_asperala(config, mode, source_path, destination_path)
|
171
|
+
else
|
172
|
+
transfer_using_shell_execute(config, mode, source_path, destination_path)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def transfer_using_asperala(config, mode, source_path, destination_path)
|
177
|
+
args_out = build_asperala_transfer_args(config, mode, source_path, destination_path)
|
178
|
+
@fasp ||= Asperalm::Fasp::Local.instance
|
179
|
+
Asperalm::Log.instance.logger = logger
|
180
|
+
# Asperalm::Log.instance.level = :debug #logger.level
|
181
|
+
@fasp.start_transfer_with_args_env(args_out, {})
|
182
|
+
{ success: true }
|
183
|
+
end
|
184
|
+
|
185
|
+
def transfer_using_shell_execute(config, mode, source_path, destination_path)
|
186
|
+
command = build_ascp_command(config, mode, source_path, destination_path)
|
73
187
|
|
74
188
|
unless ascp_path_exists?
|
75
|
-
|
76
|
-
|
189
|
+
msg = "ASCP not found. '#{ascp_path}'"
|
190
|
+
warn msg
|
191
|
+
return { message: msg, success: false }
|
77
192
|
end
|
193
|
+
|
78
194
|
agent.shell_execute(command)
|
79
195
|
end
|
80
196
|
|
197
|
+
def shutdown(graceful = true)
|
198
|
+
if @fasp && @fasp.respond_to?(:shutdown)
|
199
|
+
@fasp.shutdown(graceful)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
81
203
|
# AsperaTransferClient
|
82
204
|
end
|
83
205
|
|
@@ -17,12 +17,20 @@ module Envoi
|
|
17
17
|
region = config['region']
|
18
18
|
access_key_id = config['access_key_id']
|
19
19
|
secret_access_key = config['secret_access_key']
|
20
|
+
profile_name = config['profile'] || config['aws_profile']
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
aws_config = {}
|
23
|
+
aws_config[:credentials] = (access_key_id || secret_access_key) ?
|
24
|
+
Aws::Credentials.new(access_key_id, secret_access_key) :
|
25
|
+
Aws::SharedCredentials.new(profile_name: profile_name)
|
26
|
+
aws_config[:region] = region if region
|
27
|
+
|
28
|
+
# resource_args = { }
|
29
|
+
# resource_args[:region] = region if region && !region.empty?
|
30
|
+
# resource_args[:access_key_id] = access_key_id if access_key_id && !access_key_id.empty?
|
31
|
+
# resource_args[:secret_access_key] = secret_access_key if secret_access_key && !secret_access_key.empty?
|
32
|
+
# @s3 = Aws::S3::Resource.new(resource_args)
|
33
|
+
@s3 = Aws::S3::Resource.new(aws_config)
|
26
34
|
end
|
27
35
|
|
28
36
|
def download(config, path, destination_path = DEFAULT_DESTINATION_PATH)
|
@@ -0,0 +1,353 @@
|
|
1
|
+
require 'cantemo/portal/api/client'
|
2
|
+
require 'envoi/mam/agent/transfer_client/aspera'
|
3
|
+
require 'envoi/mam/agent/transfer_client/s3'
|
4
|
+
|
5
|
+
module Envoi
|
6
|
+
|
7
|
+
module Mam
|
8
|
+
|
9
|
+
module Cantemo
|
10
|
+
|
11
|
+
class Agent < Envoi::Mam::Agent
|
12
|
+
|
13
|
+
DEFAULT_SHAPE_TAG = 'original'
|
14
|
+
DEFAULT_DESTINATION_PATH = '.'
|
15
|
+
DEFAULT_PRESERVE_FILE_PATH = false
|
16
|
+
|
17
|
+
attr_accessor :default_aspera_ascp_args,
|
18
|
+
:default_aspera_ascp_path,
|
19
|
+
:default_vidispine_shape_tag,
|
20
|
+
:default_preserve_file_path
|
21
|
+
|
22
|
+
def after_initialize
|
23
|
+
args = initial_args
|
24
|
+
@default_aspera_ascp_path = args[:default_aspera_ascp_path]
|
25
|
+
@default_aspera_args = args.fetch(:default_ascp_args, Envoi::Mam::Agent::TransferClient::Aspera::DEFAULT_ASCP_ARGS)
|
26
|
+
@default_preserve_file_path = args.fetch(:default_preserve_file_path, DEFAULT_PRESERVE_FILE_PATH)
|
27
|
+
end
|
28
|
+
|
29
|
+
def dry_run?;
|
30
|
+
@dry_run
|
31
|
+
end
|
32
|
+
|
33
|
+
def agent_config
|
34
|
+
@agent_config ||= config['cantemo'] || config || {}
|
35
|
+
end
|
36
|
+
|
37
|
+
def agent_config_storages
|
38
|
+
agent_config['storages']
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize_api_client(args = {})
|
42
|
+
api_config = agent_config
|
43
|
+
@api_client = args[:api_client] || begin
|
44
|
+
client_args = Hash[api_config.map { |k,v| [ k.respond_to?(:to_sym) ? k.to_sym.downcase : k, v ]}]
|
45
|
+
client_args[:logger] = logger
|
46
|
+
_client = ::Cantemo::Portal::API::Client.new(client_args)
|
47
|
+
|
48
|
+
begin
|
49
|
+
_client.version
|
50
|
+
rescue => e
|
51
|
+
raise "Error connecting to Portal: #{e.message}"
|
52
|
+
end
|
53
|
+
|
54
|
+
_client
|
55
|
+
end
|
56
|
+
|
57
|
+
@default_vidispine_shape_tag = args[:default_shape_tag] || api_config['default_shape_tag'] || api_config['shape_tag'] || DEFAULT_SHAPE_TAG
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
def item_get_shape_by_tag(item_id, shape_tag)
|
62
|
+
item_shapes_get_response = api_client.item_shapes_get(:item_id => item_id, :tag => shape_tag)
|
63
|
+
shape_id = item_shapes_get_response['uri'].first
|
64
|
+
end
|
65
|
+
|
66
|
+
def download(args = {})
|
67
|
+
|
68
|
+
item_id = args[:item_id]
|
69
|
+
shape_id = args[:shape_id]
|
70
|
+
unless shape_id && !shape_id.empty?
|
71
|
+
shape_tag = args[:shape_tag] || default_vidispine_shape_tag
|
72
|
+
shape_id = item_get_shape_by_tag(item_id, shape_tag)
|
73
|
+
end
|
74
|
+
|
75
|
+
logger.info { "Getting file path for Item ID: #{item_id} Shape ID: #{shape_id}" }
|
76
|
+
item_shape_get_response = api_client.item_shape_get(:item_id => item_id, :shape_id => shape_id)
|
77
|
+
|
78
|
+
files = item_shape_get_response['containerComponent']['file']
|
79
|
+
logger.debug { "Files: #{files}" }
|
80
|
+
|
81
|
+
# file = files.first
|
82
|
+
files = [files.first] # just do the first file for now
|
83
|
+
files.each do |file|
|
84
|
+
begin
|
85
|
+
download_file(args, file)
|
86
|
+
rescue => e
|
87
|
+
logger.warn { "Exception: #{$!}" }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
logger.info { 'DONE' }
|
91
|
+
end
|
92
|
+
|
93
|
+
def download_file(args, file)
|
94
|
+
logger.debug { "File: #{file}" }
|
95
|
+
transfer_type = args[:transfer_type]
|
96
|
+
|
97
|
+
file_storage_id = file['storage']
|
98
|
+
file_path = file['path']
|
99
|
+
|
100
|
+
file_storage_config = agent_config_storages[file_storage_id]
|
101
|
+
|
102
|
+
unless file_storage_config && !file_storage_config.empty?
|
103
|
+
raise Exception, "No configuration found for storage '#{file_storage_id}'"
|
104
|
+
end
|
105
|
+
|
106
|
+
logger.info { "Transferring File Path: '#{file_path}'" }
|
107
|
+
preserve_path = args.fetch(:preserve_path, file_storage_config.fetch('preserve_path', default_preserve_file_path))
|
108
|
+
|
109
|
+
destination_path = args[:destination_path] || file_storage_config['destination_path'] || DEFAULT_DESTINATION_PATH
|
110
|
+
relative_path = preserve_path ? File.dirname(file_path) : nil
|
111
|
+
relative_path = nil if relative_path == '.'
|
112
|
+
|
113
|
+
target_path = relative_path ? File.join(destination_path, relative_path) : destination_path
|
114
|
+
target_path = target_path[0..-1] if target_path.start_with?('/') && !destination_path.start_with?('/')
|
115
|
+
|
116
|
+
aspera_config = file_storage_config['aspera']
|
117
|
+
if (transfer_type.empty? || transfer_type == :aspera) && (aspera_config && !aspera_config.empty?)
|
118
|
+
client = Envoi::Mam::Agent::TransferClient::Aspera.new(agent: self)
|
119
|
+
return client.download(aspera_config, file_path, target_path)
|
120
|
+
# download_using_aspera(aspera_config, file_path, target_path)
|
121
|
+
end
|
122
|
+
|
123
|
+
s3_config = file_storage_config['s3']
|
124
|
+
if (transfer_type.empty? || transfer_type == :s3) && (s3_config && !s3_config.empty?)
|
125
|
+
target_path = File.expand_path(target_path) if target_path == '.'
|
126
|
+
target_path = File.join(target_path, File.basename(file_path))
|
127
|
+
client = Envoi::Mam::Agent::TransferClient::S3.new(agent: self)
|
128
|
+
return client.download(s3_config, file_path, target_path)
|
129
|
+
end
|
130
|
+
|
131
|
+
logger.warn { "No Supported TransferClient Configuration#{transfer_type && !transfer_type.empty? ? " for transfer type '#{transfer_type}' " : ''}Found in Storage Configuration." }
|
132
|
+
end
|
133
|
+
|
134
|
+
# @param [Hash] args
|
135
|
+
# @option args [String] :file_path
|
136
|
+
# @option args [String] :storage_id
|
137
|
+
# @option args [String] :transfer_type ('')
|
138
|
+
# @option args [Boolean] :import_file (true)
|
139
|
+
# @option args [Boolean] :preserve_path (storage_config['preserve_path'] || default_preserve_path)
|
140
|
+
#
|
141
|
+
# @return [Hash]
|
142
|
+
def upload(args = {})
|
143
|
+
_response = {}
|
144
|
+
|
145
|
+
file_path = args[:file_path]
|
146
|
+
raise ArgumentError, "Path not found: '#{file_path}'" unless File.exists?(file_path)
|
147
|
+
|
148
|
+
if File.directory?(file_path)
|
149
|
+
# Non-recursive directory upload
|
150
|
+
file_paths = Dir.glob(File.join(file_path, '*.*'))
|
151
|
+
logger.debug { "File Paths: #{file_paths}" }
|
152
|
+
file_paths.map { |fp| upload(args.merge(file_path: fp)) }
|
153
|
+
return file_paths
|
154
|
+
end
|
155
|
+
logger.debug { "Preparing to upload '#{file_path}'" }
|
156
|
+
|
157
|
+
transfer_type = args[:transfer_type] || ''
|
158
|
+
storage_id = args[:storage_id]
|
159
|
+
storage_config = agent_config_storages[storage_id]
|
160
|
+
|
161
|
+
unless storage_config && !storage_config.empty?
|
162
|
+
raise "No configuration found for storage '#{storage_id}'"
|
163
|
+
end
|
164
|
+
|
165
|
+
should_import_file = args.fetch(:import_file, storage_config.fetch('import', true))
|
166
|
+
|
167
|
+
should_preserve_path = args.fetch(:preserve_path,
|
168
|
+
storage_config.fetch('preserve_path', default_preserve_file_path))
|
169
|
+
|
170
|
+
destination_path = args[:destination_path] || storage_config['destination_path'] || '/'
|
171
|
+
relative_path = should_preserve_path ? File.dirname(file_path) : nil
|
172
|
+
relative_path = File.expand_path(relative_path) if relative_path == '.'
|
173
|
+
|
174
|
+
target_path = relative_path ? File.join(destination_path, relative_path) : destination_path
|
175
|
+
target_path = target_path[0..-1] if target_path.start_with?('/') && !destination_path.start_with?('/')
|
176
|
+
|
177
|
+
|
178
|
+
# upload file
|
179
|
+
|
180
|
+
transfer_response = begin
|
181
|
+
response = nil
|
182
|
+
aspera_config = storage_config['aspera']
|
183
|
+
begin
|
184
|
+
if (transfer_type.empty? || transfer_type == :aspera) && (aspera_config && !aspera_config.empty?)
|
185
|
+
client = Envoi::Mam::Agent::TransferClient::Aspera.new(agent: self)
|
186
|
+
response = client.upload(aspera_config, file_path, target_path)
|
187
|
+
end
|
188
|
+
rescue => e
|
189
|
+
logger.error { "Aspera Transfer Failed. '#{e.message}'\n#{e.backtrace.first}" }
|
190
|
+
end
|
191
|
+
|
192
|
+
s3_config = storage_config['s3']
|
193
|
+
begin
|
194
|
+
if !response && (transfer_type.empty? || transfer_type == :s3) && (s3_config && !s3_config.empty?)
|
195
|
+
_target_path = target_path
|
196
|
+
_target_path = File.expand_path(_target_path) if target_path == '.'
|
197
|
+
_target_path = File.join(_target_path, File.basename(file_path))
|
198
|
+
client = Envoi::Mam::Agent::TransferClient::S3.new(agent: self)
|
199
|
+
response = client.upload(s3_config, file_path, _target_path)
|
200
|
+
end
|
201
|
+
rescue => e
|
202
|
+
logger.error { "S3 Transfer Failed. '#{e.message}'" }
|
203
|
+
end
|
204
|
+
|
205
|
+
response
|
206
|
+
end
|
207
|
+
transfer_response = { success: transfer_response } if transfer_response == true || transfer_response == false
|
208
|
+
|
209
|
+
logger.debug { "Transfer Response: #{transfer_response}" }
|
210
|
+
_response[:transfer_response] = transfer_response
|
211
|
+
|
212
|
+
if transfer_response.nil?
|
213
|
+
logger.warn { "No supported TransferClient configuration#{transfer_type && !transfer_type.empty? ? " for transfer type '#{transfer_type}'" : ''} found in storage configuration." }
|
214
|
+
_response[:success] = false
|
215
|
+
return _response
|
216
|
+
end
|
217
|
+
|
218
|
+
unless transfer_response[:success]
|
219
|
+
logger.error { "Error transferring file." }
|
220
|
+
_response[:success] = false
|
221
|
+
return _response
|
222
|
+
end
|
223
|
+
|
224
|
+
unless should_import_file
|
225
|
+
_response[:success] = transfer_response[:success]
|
226
|
+
return _response
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
### IMPORT - START
|
231
|
+
import_file_args = args.dup
|
232
|
+
import_file_args[:response_object] = _response
|
233
|
+
import_file_args[:target_path] = target_path
|
234
|
+
import_file(import_file_args)
|
235
|
+
_response[:success] = true unless _response[:success] === false
|
236
|
+
### IMPORT - END
|
237
|
+
|
238
|
+
_response
|
239
|
+
rescue => e
|
240
|
+
logger.error { "Exception: #{e.message}" }
|
241
|
+
_response[:exception] = e
|
242
|
+
return _response
|
243
|
+
end
|
244
|
+
|
245
|
+
# @param [Hash] args
|
246
|
+
# @option args [String] :file_path
|
247
|
+
# @option args [Hash] :import_args ({})
|
248
|
+
# @option args [Hash] :import_options ({})
|
249
|
+
# @option args [String] :item_id
|
250
|
+
# @option args [Hash] :response_object ({})
|
251
|
+
# @option args [String] :shape_tag (@default_vidispine_shape_tag)
|
252
|
+
# @option args [String] :storage_id
|
253
|
+
# @option args [String] :target_path
|
254
|
+
#
|
255
|
+
# @return [Hash{ :success->Boolean, :file_create_response->{}, :import_response->{} }]
|
256
|
+
def import_file(args = {})
|
257
|
+
logger.debug { "Agent - Import File - Args: #{args}"}
|
258
|
+
_response = args[:response_object] || {}
|
259
|
+
|
260
|
+
file_path = args[:file_path]
|
261
|
+
storage_id = args[:storage_id]
|
262
|
+
|
263
|
+
target_path = args[:target_path]
|
264
|
+
|
265
|
+
item_id = args[:item_id]
|
266
|
+
shape_tag = args[:shape_tag] || default_vidispine_shape_tag
|
267
|
+
|
268
|
+
item_add_args_in = args[:item_add_args] || { }
|
269
|
+
item_add_options_in = args[:item_add_options] || { }
|
270
|
+
|
271
|
+
# attach file to item as shape
|
272
|
+
path_on_storage = File.join(target_path, File.basename(file_path))
|
273
|
+
path_on_storage = path_on_storage[1..-1] if path_on_storage.start_with?('/')
|
274
|
+
# file_create_response = api_client.storage_file_create(storage_id: storage_id,
|
275
|
+
# path: path_on_storage, state: 'CLOSED')
|
276
|
+
file_create_response = api_client.storage_file_get_or_create(storage_id, path_on_storage, { :extended_response => true })
|
277
|
+
_response[:file_create_response] = file_create_response
|
278
|
+
file = file_create_response[:file]
|
279
|
+
|
280
|
+
file_id = file['id']
|
281
|
+
|
282
|
+
unless file_id
|
283
|
+
_file = file.dup
|
284
|
+
_file.keep_if { |k, v| v }
|
285
|
+
logger.error { "Failed to create file. #{_file}" }
|
286
|
+
_response[:success] = false
|
287
|
+
return _response
|
288
|
+
end
|
289
|
+
|
290
|
+
item = (file['item'] || []).first
|
291
|
+
|
292
|
+
if item
|
293
|
+
shape = (item['shape'] || []).first
|
294
|
+
msg = "File already exist and is associated to item #{item['id']} as shape #{shape['id']}."
|
295
|
+
logger.warn { "#{msg} #{file}" }
|
296
|
+
_response[:error] = { :message => msg }
|
297
|
+
_response[:success] = false
|
298
|
+
return _response
|
299
|
+
end
|
300
|
+
|
301
|
+
if item_id
|
302
|
+
item_shape_import_response = api_client.item_shape_import(item_id: item_id,
|
303
|
+
tag: shape_tag, file_id: file_id)
|
304
|
+
else
|
305
|
+
item_add_args = {
|
306
|
+
storage_id: storage_id,
|
307
|
+
file_path: path_on_storage,
|
308
|
+
file_id: file_id,
|
309
|
+
storage_path_map: { '/' => storage_id }
|
310
|
+
}
|
311
|
+
item_add_args.merge!(item_add_args_in) if item_add_args_in.is_a?(Hash)
|
312
|
+
|
313
|
+
item_add_options = { }
|
314
|
+
item_add_options.merge!(item_add_options_in) if item_add_options_in.is_a?(Hash)
|
315
|
+
|
316
|
+
logger.debug { "Executing Item Add/Import. ARGS: #{item_add_args} OPTS: #{item_add_options}" }
|
317
|
+
item_shape_import_response = api_client.item_add_using_file_path(item_add_args, item_add_options)
|
318
|
+
end
|
319
|
+
_response[:import_response] = item_shape_import_response
|
320
|
+
|
321
|
+
_response
|
322
|
+
end
|
323
|
+
|
324
|
+
def ingest_file
|
325
|
+
# 1) export ASPERA_SCP_TOKEN="ATB3_AEA9dUI5dd2u1k8XBMVD0qInR-Lyylkz8AylTXLVt5_SMVGR8SO7Sdc0lj6RkoHK_DvAAEWac8bllF_Yan1NbbDTyPj_3BTA"
|
326
|
+
# 2) ascp -i asperaweb_id_dsa.openssh -P 33001 -l 20m /Users/nicholasstokes/Desktop/CantemoAgent.mov xfer@ats-aws-us-west-2.aspera.io:
|
327
|
+
#
|
328
|
+
# single line equivalent
|
329
|
+
# ascp -i asperaweb_id_dsa.openssh -W 'ATB3_AEAiBdrf-7sMQu782sPoQ4Q7Yh4LadgvYK9BP4Kt1hcVlZGR8SO7Sdc0lj6RkoHK_DvAAG5XlBBIZGEINicYOxMRE7g_3BTA' -P 33001 -l 20m /Users/nicholasstokes/Desktop/CantemoAgent.mov xfer@ats-aws-us-west-2.aspera.io:
|
330
|
+
|
331
|
+
# Aspera:
|
332
|
+
# `GET /aspera/ping/` - to check if Aspera upload is available
|
333
|
+
# `POST /vs/item/new/` to create a placeholder to upload to
|
334
|
+
# `POST /aspera/upload_setup/` - Aspera token
|
335
|
+
# ... upload the file with Aspera ...
|
336
|
+
# `POST /API/v1/placeholder/VX-3012/associatefile/?filepath=Screen%20Shot%202018-08-07%20at%2013.06.58.png&storageid=VX-11&platform=mac` - associate new file to placeholder
|
337
|
+
|
338
|
+
# HTTP upload:
|
339
|
+
# `POST /vs/item/new/` - create a placeholder
|
340
|
+
# then streamind POST (?) requests to `VSAPI/item/VX-3014/shape/essence/raw?transferId=2D30030A-6EC3-4364-9598-5BF20972A218&throttle=false&filename=Screen%20Shot%202018-08-07%20at%2014.34.15.png&jobmetadata=portal_groups:StringArray%3dAdmin` to upload the data.
|
341
|
+
# that end-point for HTTP is documented in http://apidoc.vidispine.com/latest/ref/item/shape.html#import-a-shape-using-the-request-body
|
342
|
+
#
|
343
|
+
end
|
344
|
+
|
345
|
+
# Agent
|
346
|
+
end
|
347
|
+
|
348
|
+
# Cantemo
|
349
|
+
end
|
350
|
+
|
351
|
+
end
|
352
|
+
|
353
|
+
end
|