sdr-replication 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e99a0814e4383ec6287dec7df41825d786e65919
4
- data.tar.gz: 4890c2758dd820f22ce8aa9015621e1b33dec9ae
3
+ metadata.gz: 477696a5ac274697dfb1f337e562a859965695e8
4
+ data.tar.gz: ee4274bb597963bb1faec8783a0a41875abbd369
5
5
  SHA512:
6
- metadata.gz: deb3400e53fcbdf16cf8263ffe1deed70be83889633b4fecfa0de58e41a076e7f502de61d22f079b09ff6b81ddb29586bb6e898782f22e45010ecfd46d93828d
7
- data.tar.gz: a0533d23addcc264e7aeca97f985bdad1d70e8081230e09ab5337a03314b058374541b1587f47a5f89dd2f558115677190d49b320b892a77e7031bb15ac9c3ff
6
+ metadata.gz: 094e30cbc6e5e8c490e5560506a34b69cff7ab463cb4215e34d269b3f9abc9754fece858ac734a56448e1e4f0bd0332f6e7b0aee6d72e9d9436f5722afa6bfed
7
+ data.tar.gz: 1b1593dc47c1190a7bc1ea60b6f2bdb6635f21fc1170a7ef3cd18bb3be481d223b1b30435b72e327226ac33a81f63129a53405821cb4f7f04bbcc0c01ae64f54
@@ -0,0 +1,18 @@
1
+ require File.join(File.dirname(__FILE__),'../libdir')
2
+ require 'sdr_replication'
3
+
4
+ module Replication
5
+
6
+ class SdrObject < Moab::StorageObject
7
+
8
+ def initialize(druid)
9
+ @digital_object_id = druid
10
+ storage_object = StorageServices.find_storage_object(druid, include_deposit=true)
11
+ @object_pathname = storage_object.object_pathname
12
+ @storage_root = storage_object.storage_root
13
+ end
14
+
15
+
16
+ end
17
+
18
+ end
@@ -6,40 +6,139 @@ module Replication
6
6
  #
7
7
  # @note Copyright (c) 2014 by The Board of Trustees of the Leland Stanford Junior University.
8
8
  # All rights reserved. See {file:LICENSE.rdoc} for details.
9
- class SdrObjectVersion
9
+ class SdrObjectVersion < Moab::StorageObjectVersion
10
10
 
11
- # @return [Moab::StorageObjectVersion] Represents the object version's storage location
12
- attr_accessor :moab_object_version
11
+ def digital_object_id
12
+ storage_object.digital_object_id
13
+ end
13
14
 
14
- # @param [Moab::StorageObjectVersion] object_version Represents the object version's storage location
15
- # @return [SdrObjectVersion] Initialize a new SdrObjectVersion object
16
- def initialize(object_version)
17
- @moab_object_version = object_version
15
+ # @return [Moab::FileInventory] The moab version manifest for the version
16
+ def version_inventory
17
+ @version_inventory ||= file_inventory('version')
18
18
  end
19
19
 
20
- # @return [String] The digital object identifier (druid)
21
- def sdr_object_id
22
- @sdr_object_id ||= moab_object_version.storage_object.digital_object_id
20
+ # @return [Moab::FileInventory] The moab version manifest for the version
21
+ def version_additions
22
+ @version_additions ||= file_inventory('additions')
23
23
  end
24
24
 
25
- # @return [Integer] The digital object version number
26
- def sdr_version_id
27
- @sdr_version_id ||= moab_object_version.version_id
25
+ # @return [Hash] The contents of the versionMetadata file
26
+ def parse_version_metadata
27
+ metadata = Hash.new
28
+ pathname = find_filepath('metadata', 'versionMetadata.xml')
29
+ if pathname.exist?
30
+ doc = Nokogiri::XML(pathname.read)
31
+ nodeset = doc.xpath("/versionMetadata/version")
32
+ metadata[:version_id] = nodeset.last['versionId'].to_i unless nodeset.empty?
33
+ end
34
+ metadata
28
35
  end
29
36
 
30
- # @return [Moab::FileInventory] The moab version manifest for the version
31
- def version_inventory
32
- @version_inventory ||= moab_object_version.file_inventory('version')
37
+ # @return [Hash] The contents of the identityMetadata file
38
+ def parse_identity_metadata
39
+ metadata = Hash.new
40
+ pathname = find_filepath('metadata', 'identityMetadata.xml')
41
+ if pathname.exist?
42
+ doc = Nokogiri::XML(pathname.read)
43
+ nodeset = doc.xpath("/identityMetadata/objectType")
44
+ metadata[:object_type] = nodeset.first.text unless nodeset.empty?
45
+ nodeset = doc.xpath("/identityMetadata/objectLabel")
46
+ metadata[:object_label] = nodeset.first.text unless nodeset.empty?
47
+ end
48
+ metadata
33
49
  end
34
50
 
35
- # @return [Moab::FileInventory] The moab version manifest for the version
36
- def version_additions
37
- @version_additions ||= moab_object_version.file_inventory('additions')
51
+ # @return [Hash] The contents of the relationshipMetadata file
52
+ def parse_relationship_metadata
53
+ metadata = Hash.new
54
+ pathname = find_filepath('metadata', 'relationshipMetadata.xml')
55
+ if pathname.exist?
56
+ doc = Nokogiri::XML(pathname.read)
57
+ nodeset = doc.xpath("//hydra:isGovernedBy", 'hydra' => 'http://projecthydra.org/ns/relations#')
58
+ unless nodeset.empty?
59
+ apo_id = nodeset.first.attribute_with_ns('resource', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#')
60
+ if apo_id
61
+ metadata[:governed_by] = apo_id.text.split('/')[-1]
62
+ end
63
+ end
64
+ end
65
+ metadata
66
+ end
67
+
68
+ def update_object_data
69
+
70
+ identity_metadata = parse_identity_metadata
71
+ relationship_metadata = parse_relationship_metadata
72
+
73
+ digital_object_data = {
74
+ :digital_object_id => digital_object_id,
75
+ :home_repository => 'sdr'
76
+ }
77
+
78
+ sdr_object_data = {
79
+ :sdr_object_id => digital_object_id,
80
+ :object_type => identity_metadata[:object_type],
81
+ :object_label => identity_metadata[:object_label],
82
+ :governing_object => relationship_metadata[:governed_by],
83
+ :latest_version => storage_object.current_version_id
84
+ }
85
+
86
+ if version_id == 1
87
+ ArchiveCatalog.find_or_create_item(:digital_objects,digital_object_data)
88
+ ArchiveCatalog.find_or_create_item(:sdr_objects,sdr_object_data)
89
+ else
90
+ ArchiveCatalog.update_item(:sdr_objects, digital_object_id, sdr_object_data)
91
+ end
92
+
93
+ end
94
+
95
+ def update_version_data
96
+
97
+ sdr_object_version_data = {
98
+ :sdr_object_id => digital_object_id,
99
+ :sdr_version_id => version_id,
100
+ :replica_id => composite_key,
101
+ :ingest_date => version_inventory.inventory_datetime
102
+ }
103
+ ArchiveCatalog.find_or_create_item(:sdr_object_versions, sdr_object_version_data)
104
+
105
+
106
+ content = version_inventory.group('content')
107
+ metadata = version_inventory.group('metadata')
108
+ sdr_version_full = {
109
+ :sdr_object_id => digital_object_id,
110
+ :sdr_version_id => version_id,
111
+ :inventory_type => 'full',
112
+ :content_files => content.file_count,
113
+ :content_bytes => content.byte_count,
114
+ :content_blocks => content.block_count,
115
+ :metadata_files => metadata.file_count,
116
+ :metadata_bytes => metadata.byte_count,
117
+ :metadata_blocks => metadata.block_count
118
+ }
119
+ ArchiveCatalog.find_or_create_item(:sdr_version_stats, sdr_version_full)
120
+
121
+ content = version_additions.group('content')
122
+ metadata = version_additions.group('metadata')
123
+ sdr_version_delta = {
124
+ :sdr_object_id => digital_object_id,
125
+ :sdr_version_id => version_id,
126
+ :inventory_type => 'delta',
127
+ :content_files => content.file_count,
128
+ :content_bytes => content.byte_count,
129
+ :content_blocks => content.block_count,
130
+ :metadata_files => metadata.file_count,
131
+ :metadata_bytes => metadata.byte_count,
132
+ :metadata_blocks => metadata.block_count
133
+ }
134
+ ArchiveCatalog.find_or_create_item(:sdr_version_stats, sdr_version_delta)
135
+
38
136
  end
39
137
 
138
+
40
139
  # @return [String] The unique identifier for the digital object replica
41
140
  def replica_id
42
- @replica_id ||= "#{sdr_object_id.split(':').last}-#{moab_object_version.version_name}"
141
+ @replica_id ||= "#{storage_object.digital_object_id.split(':').last}-#{version_name}"
43
142
  end
44
143
 
45
144
  # @return [Replica] The Replica of the object version that is archived to tape, etc
@@ -52,7 +151,7 @@ module Replication
52
151
  bag_dir = replica.replica_pathname
53
152
  bag = BagitBag.create_bag(bag_dir)
54
153
  bag.bag_checksum_types = [:sha256]
55
- bag.add_payload_tarfile("#{replica_id}.tar",moab_object_version.version_pathname, moab_object_version.storage_object.object_pathname.parent)
154
+ bag.add_payload_tarfile("#{replica_id}.tar",version_pathname, storage_object.object_pathname.parent)
56
155
  bag.write_bag_info_txt
57
156
  bag.write_manifest_checksums('tagmanifest', bag.generate_tagfile_checksums)
58
157
  bag
@@ -20,6 +20,7 @@ require 'replication/file_fixity'
20
20
  require 'replication/fixity'
21
21
  require 'replication/operating_system'
22
22
  require 'replication/replica'
23
+ require 'replication/sdr_object'
23
24
  require 'replication/sdr_object_version'
24
25
  require 'replication/tarfile'
25
26
  include Replication
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sdr-replication
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Anderson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-24 00:00:00.000000000 Z
11
+ date: 2014-07-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json_pure
@@ -26,6 +26,20 @@ dependencies:
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: moab-versioning
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: systemu
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - ">="
@@ -39,7 +53,7 @@ dependencies:
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
- name: systemu
56
+ name: rest-client
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - ">="
@@ -53,13 +67,13 @@ dependencies:
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
- name: rest-client
70
+ name: rake
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - ">="
60
74
  - !ruby/object:Gem::Version
61
75
  version: '0'
62
- type: :runtime
76
+ type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
@@ -161,13 +175,11 @@ files:
161
175
  - lib/libdir.rb
162
176
  - lib/replication/archive_catalog.rb
163
177
  - lib/replication/bagit_bag.rb
164
- - lib/replication/command_consumer.rb
165
- - lib/replication/command_producer.rb
166
- - lib/replication/dpn_check_rep.rb
167
178
  - lib/replication/file_fixity.rb
168
179
  - lib/replication/fixity.rb
169
180
  - lib/replication/operating_system.rb
170
181
  - lib/replication/replica.rb
182
+ - lib/replication/sdr_object.rb
171
183
  - lib/replication/sdr_object_version.rb
172
184
  - lib/replication/tarfile.rb
173
185
  - lib/sdr_replication.rb
@@ -190,7 +202,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
190
202
  version: 2.2.1
191
203
  requirements: []
192
204
  rubyforge_project:
193
- rubygems_version: 2.2.1
205
+ rubygems_version: 2.3.0
194
206
  signing_key:
195
207
  specification_version: 4
196
208
  summary: Core methods for support of SDR Preservation Core replication
@@ -1,55 +0,0 @@
1
- require "rubygems"
2
- require "amqp"
3
- require "amqp/extensions/rabbitmq"
4
- require "yaml"
5
-
6
- EventMachine.run do
7
-
8
-
9
- connection_params = {
10
- :host => "127.0.0.1",
11
- :port => 5672,
12
- :user => "guest",
13
- :pass => "guest",
14
- :vhost => "/",
15
- :ssl => false,
16
- :frame_max => 131072
17
- }
18
-
19
- connection = AMQP.connect(connection_params)
20
- channel = AMQP::Channel.new(connection, :auto_recovery => true)
21
-
22
-
23
- channel.prefetch(1)
24
-
25
- # Acknowledgements are good for letting the server know
26
- # that the task is finished. If the consumer doesn't send
27
- # the acknowledgement, then the task is considered to be unfinished
28
- # and will be requeued when consumer closes AMQP connection (because of a crash, for example).
29
-
30
- channel.queue("sdr.replication.notification", :durable => true, :auto_delete => false).subscribe(:ack => true) do |metadata, payload|
31
- case metadata.type
32
- when "sdr_replication_notification"
33
- data = YAML.load(payload)
34
- puts "sdr_replication_notification request with #{data.inspect}"
35
-
36
- message = "'" + payload + "'"
37
- header = "'" + metadata.attributes.to_yaml + "'"
38
-
39
-
40
- (pid = fork) ? Process.detach(pid) : exec("./dpn_check_rep.rb #{message} #{header}")
41
- #shellout = "./dpn_check_rep.rb '#{data[:name]}' #{data[:size]}"
42
- #puts "dpn_check_rep.rb Executing #{shellout}"; system(shellout)
43
- puts "Call to SDR DPN replication request done"
44
- puts
45
-
46
- when "dpn_replication_notification"
47
- else
48
- puts "[commands] Unknown command: #{metadata.type}"
49
- end
50
-
51
- # message is processed, acknowledge it so that broker discards it
52
- metadata.ack
53
- end
54
- Signal.trap("INT") { connection.close { EventMachine.stop } }
55
- end
@@ -1,105 +0,0 @@
1
- require "amqp"
2
- require "amqp/extensions/rabbitmq"
3
- require "yaml"
4
- require "json"
5
- require "securerandom"
6
- require "time"
7
-
8
-
9
- connection_params = {
10
- :host => "127.0.0.1",
11
- :port => 5672,
12
- :user => "guest",
13
- :pass => "guest",
14
- :vhost => "/",
15
- :ssl => false,
16
- :frame_max => 131072
17
- }
18
-
19
- #usually send one message only, rely on message broker to recover
20
- doit = 1
21
-
22
- EventMachine.run do
23
- sleep(0.5)
24
-
25
- connection = AMQP.connect(connection_params)
26
- channel = AMQP::Channel.new(connection)
27
-
28
-
29
- # publish new commands every 2 seconds, count + one times
30
- count = 0
31
- EventMachine.add_periodic_timer(2.0) do
32
- count += 1
33
- if count >= doit then
34
- EM.stop_event_loop
35
- end
36
- puts "count = #{count}"
37
- puts "Messaging that sdr has content for replication, parameters are name and size."
38
- payload = { :name => "12345.tar", :size => 4096 }.to_yaml
39
-
40
- correlation_id = SecureRandom.random_number(10000)
41
- utctime = Time.now.utc
42
- ttl = 20 #time for transaction to live
43
-
44
- channel.default_exchange.publish(payload,
45
- :arguments => { "x-message-ttl" => 10 },
46
- :type => "sdr_replication_notification",
47
- :routing_key => "sdr.replication.notification",
48
- :reply_to => "dpn.replication.reply",
49
- :correlation_id => correlation_id,
50
- :timestamp => utctime.to_i
51
- )
52
-
53
- end
54
-
55
- puts "Sending notification message"
56
- Signal.trap("INT") { connection.close { EventMachine.stop } }
57
- end
58
-
59
-
60
-
61
- exit
62
-
63
- #listen for response to previous message
64
- EventMachine.run do
65
- sleep(0.5)
66
-
67
- connection = AMQP.connect(connection_params)
68
- channel = AMQP::Channel.new(connection, :auto_recovery => true)
69
- channel.prefetch(1)
70
-
71
- count = 0
72
- EventMachine.add_periodic_timer(10.0) do
73
- count += 1
74
- puts "waiting....."
75
- if count > doit+1 then
76
- EM.stop_event_loop
77
- end
78
- end
79
-
80
- channel.queue("dpn.replication.notification", :durable => true, :auto_delete => false).subscribe(:ack => true) do |metadata, payload|
81
- case metadata.type
82
- when "dpn_replication_ack"
83
- message = YAML.load(payload)
84
- if message[:message] == "dpn_available_reply" && message[:acknak] == 'ack' then
85
- puts "[sdr dpn says to go ahead and replicate content to temp area."
86
- # (pid = fork) ? Process.detach(pid) : exec("start_replication_workflow.rb
87
- puts "Start workflow to copy content to sdr dpn for replication. "
88
- else
89
- puts "no replication possible, DPN say's no!"
90
- # (pid = fork) ? Process.detach(pid) : exec("replication_denied_workflow.rb
91
- end
92
-
93
- when "dpn_replication_notification"
94
- puts "DPN wants to replicate"
95
- else
96
- puts "[commands] Unknown command: #{metadata.type}"
97
- end
98
-
99
- # message is processed, acknowledge it so that broker discards it
100
- metadata.ack
101
- end
102
- puts "Waiting for 'ack' notification message"
103
- Signal.trap("INT") { connection.close { EventMachine.stop } }
104
-
105
- end
@@ -1,83 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # encoding: utf-8
3
- $: << "./"
4
- require 'securerandom'
5
- require "rubygems"
6
- require 'time'
7
- require 'date'
8
- require "eventmachine"
9
- require "amqp"
10
- require "amqp/extensions/rabbitmq"
11
- require "yaml"
12
-
13
-
14
- connection_params = {
15
- :host => "127.0.0.1",
16
- :port => 5672,
17
- :user => "guest",
18
- :pass => "guest",
19
- :vhost => "/",
20
- :ssl => false,
21
- :frame_max => 131072
22
- }
23
-
24
- #arguments passed are json strings. payload, header.
25
- puts "*************************************"
26
- puts "In dpn_check_rep.rb"
27
- if ARGV.length != 2 then
28
- exit 5001 #should not get here, but this is an error condition
29
- end
30
-
31
- message = YAML.load(ARGV[0])
32
- header = YAML.load(ARGV[1])
33
-
34
- if header[:type] != "sdr_replication_notification" then
35
- exit 5002 #bad message type
36
- end
37
-
38
- puts "sdr_replication_notification message #{message.inspect}"
39
- puts "sdr_replication_notification header #{header.inspect}"
40
-
41
- puts "checking to see if DPN can replicate content from SDR"
42
-
43
- #
44
- #Do checking!
45
- #
46
-
47
- sleep 4.5 #simulate checking
48
-
49
-
50
- exit
51
-
52
- EventMachine.run do
53
- sleep(0.5)
54
- connection = AMQP.connect(connection_params)
55
- channel = AMQP::Channel.new(connection)
56
-
57
- count = 0
58
- EventMachine.add_periodic_timer(2.0) do
59
- count += 1
60
- if count > 2 then
61
- EM.stop_event_loop
62
- end
63
- end
64
-
65
- EM.add_timer(1.0) do
66
- puts "Working: #{Time.now}"
67
- STDOUT.flush
68
- end
69
-
70
- puts "Acking that content can be replicated"
71
- payload = { :message => "dpn_available_reply", :acknak => 'ack' }.to_yaml
72
-
73
- channel.default_exchange.publish(payload,
74
- :type => "dpn_replication_ack",
75
- :routing_key => header[:reply_to],
76
- :reply_to => "dpn.err.notification",
77
- :correlation_id => header[:correlation_id],
78
- :timestamp => header[:timestamp])
79
-
80
- puts "Sending ack notification message"
81
- Signal.trap("INT") { connection.close { EventMachine.stop } }
82
-
83
- end