cdr-fetcher 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Carl Hicks
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,51 @@
1
+ = cdr-fetcher
2
+
3
+ A rubygem for fetching CDR's from a remote system via SSH/SFTP.
4
+ Supports fetching all files, or a delta from a given offset.
5
+
6
+ == FEATURES/PROBLEMS:
7
+
8
+ * Supports asterisk cdr-csv output format
9
+ * Supports multiple directories
10
+ * Returns a format-neutral data structure
11
+
12
+ == SYNOPSIS:
13
+
14
+ require 'cdr-fetcher'
15
+ require 'pp'
16
+
17
+ cdrs = CDR::Fetcher.new(
18
+ 'hostname',
19
+ 'username',
20
+ :password => 'password',
21
+ :base_dir => '/var/log/asterisk/cdr-csv',
22
+ :last_dir => 'old-cdrs',
23
+ :last_file => 'old-cdr.csv')
24
+
25
+ cdrs.each do |cdr|
26
+ pp cdr
27
+ end
28
+
29
+ == REQUIREMENTS:
30
+
31
+ * net-ssh gem
32
+ * net-sftp gem
33
+
34
+ == INSTALL:
35
+
36
+ * sudo gem install cdr-fetcher
37
+
38
+
39
+ == Note on Patches/Pull Requests
40
+
41
+ * Fork the project.
42
+ * Make your feature addition or bug fix.
43
+ * Add tests for it. This is important so I don't break it in a
44
+ future version unintentionally.
45
+ * Commit, do not mess with rakefile, version, or history.
46
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
47
+ * Send me a pull request. Bonus points for topic branches.
48
+
49
+ == Copyright
50
+
51
+ Copyright (c) 2010 Carl Hicks. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "cdr-fetcher"
8
+ gem.summary = %Q{A gem for fetching CDR's from a remote system.}
9
+ gem.description = %Q{A gem for fetching CDR data from a remote system via SSH/SFTP. Supports fetching all files, or a delta from a given offset.}
10
+ gem.email = "carl.hicks@gmail.com"
11
+ gem.homepage = "http://github.com/chicks/cdr-fetcher"
12
+ gem.authors = ["Carl Hicks"]
13
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
+ gem.add_dependency 'net-sftp'
15
+ gem.add_dependency 'net-ssh'
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ require 'rake/testtask'
24
+ Rake::TestTask.new(:test) do |test|
25
+ test.libs << 'lib' << 'test'
26
+ test.pattern = 'test/**/test_*.rb'
27
+ test.verbose = true
28
+ end
29
+
30
+ begin
31
+ require 'rcov/rcovtask'
32
+ Rcov::RcovTask.new do |test|
33
+ test.libs << 'test'
34
+ test.pattern = 'test/**/test_*.rb'
35
+ test.verbose = true
36
+ end
37
+ rescue LoadError
38
+ task :rcov do
39
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
40
+ end
41
+ end
42
+
43
+ task :test => :check_dependencies
44
+
45
+ task :default => :test
46
+
47
+ require 'rake/rdoctask'
48
+ Rake::RDocTask.new do |rdoc|
49
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
50
+
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = "cdr-fetcher #{version}"
53
+ rdoc.rdoc_files.include('README*')
54
+ rdoc.rdoc_files.include('lib/**/*.rb')
55
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.1
@@ -0,0 +1,59 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{cdr-fetcher}
8
+ s.version = "1.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Carl Hicks"]
12
+ s.date = %q{2010-07-15}
13
+ s.description = %q{A gem for fetching CDR data from a remote system via SSH/SFTP. Supports fetching all files, or a delta from a given offset.}
14
+ s.email = %q{carl.hicks@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ "LICENSE",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "cdr-fetcher.gemspec",
26
+ "lib/cdr-fetcher.rb",
27
+ "test/helper.rb",
28
+ "test/test_cdr-fetcher.rb"
29
+ ]
30
+ s.homepage = %q{http://github.com/chicks/cdr-fetcher}
31
+ s.rdoc_options = ["--charset=UTF-8"]
32
+ s.require_paths = ["lib"]
33
+ s.rubygems_version = %q{1.3.7}
34
+ s.summary = %q{A gem for fetching CDR's from a remote system.}
35
+ s.test_files = [
36
+ "test/helper.rb",
37
+ "test/test_cdr-fetcher.rb"
38
+ ]
39
+
40
+ if s.respond_to? :specification_version then
41
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
42
+ s.specification_version = 3
43
+
44
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
45
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
46
+ s.add_runtime_dependency(%q<net-sftp>, [">= 0"])
47
+ s.add_runtime_dependency(%q<net-ssh>, [">= 0"])
48
+ else
49
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
50
+ s.add_dependency(%q<net-sftp>, [">= 0"])
51
+ s.add_dependency(%q<net-ssh>, [">= 0"])
52
+ end
53
+ else
54
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
55
+ s.add_dependency(%q<net-sftp>, [">= 0"])
56
+ s.add_dependency(%q<net-ssh>, [">= 0"])
57
+ end
58
+ end
59
+
@@ -0,0 +1,210 @@
1
+ module CDR
2
+
3
+ require 'csv'
4
+ require 'rubygems'
5
+ require 'net/ssh'
6
+ require 'net/sftp'
7
+
8
+ # Fetch CDR's from a remote server.
9
+ #
10
+ # :base_dir defaults to "/var/log/asterisk/cdr-csv".
11
+ # :last_dir defaults to the first sub-directory
12
+ # in :base_dir.
13
+ # :last_file defaults to the first file in :last_dir.
14
+ #
15
+ # Usage:
16
+ #
17
+ # require 'cdr-fetcher'
18
+ # require 'pp'
19
+ #
20
+ # cdrs = CDR::Fetcher.new(
21
+ # 'hostname',
22
+ # 'username',
23
+ # :password => 'password',
24
+ # :base_dir => '/var/log/asterisk/cdr-csv',
25
+ # :last_dir => 'old-cdrs',
26
+ # :last_file=> 'old-cdr.csv')
27
+ #
28
+ # cdrs.each do |cdr|
29
+ # pp cdr
30
+ # end
31
+ #
32
+ class Fetcher
33
+ attr :hostname, false
34
+ attr :username, false
35
+ attr :password, false
36
+ attr :base_dir, false
37
+ attr :last_dir, true
38
+ attr :last_file, true
39
+
40
+ # Create a new instance of Fetcher
41
+ #
42
+ # :base_dir defaults to "/var/log/asterisk/cdr-csv".
43
+ # :last_dir defaults to the first sub-directory
44
+ # in :base_dir.
45
+ # :last_file defaults to the first file in :last_dir.
46
+ def initialize(hostname, username, opts={})
47
+ # Process options and arguments
48
+ opts = { :password => nil,
49
+ :base_dir => nil,
50
+ :last_dir => nil,
51
+ :last_file=> nil
52
+ }.merge!(opts)
53
+ @hostname = hostname
54
+ @username = username
55
+ @password = opts[:password]
56
+ @base_dir = opts[:base_dir]
57
+ @base_dir ||= '/var/log/asterisk/cdr-csv'
58
+
59
+ # Setup SSH and SFTP Connection
60
+ @ssh = Net::SSH.start(@hostname, @username, :password => @password)
61
+ @sftp = Net::SFTP::Session.new(@ssh).connect!
62
+
63
+ # Grab the list of directories, and the current directory attributes
64
+ @directories = directories
65
+ @cur_dir = directory(opts[:last_dir])
66
+ # If we weren't passed an argument, use the first file in the list
67
+ @cur_dir ||= @directories[0]
68
+
69
+ # Grab the list of files from the current directory
70
+ @files = files
71
+ @cur_file = file(opts[:last_file])
72
+ # If we weren't passed an argument, use the first file in the list
73
+ @cur_file ||= @files[0]
74
+ end
75
+
76
+ # Iterator for Files. Returns one file path at a time, automatically
77
+ # moving to the next file or directory until all CDRs have been
78
+ # returned.
79
+ def each_file
80
+ while true
81
+ file = @cur_file
82
+ if file
83
+ yield [@base_dir,@cur_dir.name,@cur_file.name].join("/")
84
+ next_file!
85
+ else
86
+ break
87
+ end
88
+ end
89
+ end
90
+
91
+ # Iterator for CDRs. Returns one CDR at a time, automatically
92
+ # moving to the next file or directory until all CDRs have been
93
+ # returned.
94
+ #
95
+ # CDR files are read into memory one at a time.
96
+ #
97
+ # Line numbers are NOT zero indexed (i.e. line 1 == line 1 NOT line 0)
98
+ def each
99
+ each_file do |file|
100
+ @sftp.download!(file).split(/\n/).each_with_index do |line,i|
101
+ cdr = {
102
+ :hostname => @hostname,
103
+ :file => file,
104
+ :line => i+1,
105
+ :cdr => CSV.parse_line(line)
106
+ }
107
+ yield cdr
108
+ end
109
+ end
110
+ end
111
+
112
+ protected
113
+
114
+ # Set @cur_file to the next file.
115
+ # Changes to next directory if all files in the current directory
116
+ # have been read.
117
+ def next_file!
118
+ @files.each_with_index { |file,i|
119
+ # find our current location
120
+ if @cur_file == file
121
+ next_file = @files[i+1]
122
+ # If we dont hit the end of the array, everything is good.
123
+ if next_file != nil
124
+ @cur_file = next_file
125
+ return @cur_file
126
+ # if we hit the end of the array, we need to go to the next directory
127
+ else
128
+ # If we run out of directories, we need to exit properly.
129
+ dir = next_dir!
130
+ if dir
131
+ return @cur_file
132
+ else
133
+ return false
134
+ end
135
+ end
136
+ end
137
+ }
138
+ end
139
+
140
+ # Set @cur_dir to the next directory.
141
+ # Updates @cur_file to the first file in that directory
142
+ def next_dir!
143
+ @directories.each_with_index {|dir,i|
144
+ # find our current location
145
+ if @cur_dir == dir
146
+ next_dir = @directories[i+1]
147
+ if next_dir != nil
148
+ @cur_dir = next_dir
149
+ @files = files
150
+ @cur_file = @files[0]
151
+ return @cur_dir
152
+ else
153
+ return false
154
+ end
155
+ end
156
+ }
157
+ end
158
+
159
+ # Return an Array of subdirectories in the @base_dir
160
+ def directories
161
+ directories = []
162
+ @sftp.dir.foreach(@base_dir) do |entry|
163
+ # skip parent, or hidden files/directories
164
+ next if entry.name =~ /^\.+$/
165
+ # skip files
166
+ next unless entry.directory?
167
+ # store everything else
168
+ directories << entry
169
+ end
170
+ # Sort directories by modification time
171
+ directories.sort! {|a,b| a.attributes.mtime <=> b.attributes.mtime}
172
+ directories
173
+ end
174
+
175
+ # Searches for a directory entry by name
176
+ # Returns a directory object from the list of directories
177
+ def directory(dir_name)
178
+ @directories.each do |dir|
179
+ return dir if dir.name == dir_name
180
+ end
181
+ false
182
+ end
183
+
184
+ # Return a list of files in @cur_dir
185
+ def files
186
+ files = []
187
+ @sftp.dir.foreach(@base_dir + "/" + @cur_dir.name) do |file|
188
+ # ignore directories
189
+ next unless file.file?
190
+ files << file
191
+ end
192
+ # Sort each file by modification time
193
+ files.sort! {|a,b| a.attributes.mtime <=> b.attributes.mtime }
194
+ files
195
+ end
196
+
197
+ # Searches for a file entry by name
198
+ # Returns a file object from the list of files
199
+ def file(file_name)
200
+ @files.each do |file|
201
+ return file if file.name == file_name
202
+ end
203
+ false
204
+ end
205
+
206
+ end
207
+
208
+ end
209
+
210
+
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'cdr-fetcher'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestCdrFetcher < Test::Unit::TestCase
4
+ #should "probably rename this file and start testing for real" do
5
+ # flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ #end
7
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cdr-fetcher
3
+ version: !ruby/object:Gem::Version
4
+ hash: 21
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 1
10
+ version: 1.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Carl Hicks
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-07-15 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: thoughtbot-shoulda
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: net-sftp
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: net-ssh
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ type: :runtime
62
+ version_requirements: *id003
63
+ description: A gem for fetching CDR data from a remote system via SSH/SFTP. Supports fetching all files, or a delta from a given offset.
64
+ email: carl.hicks@gmail.com
65
+ executables: []
66
+
67
+ extensions: []
68
+
69
+ extra_rdoc_files:
70
+ - LICENSE
71
+ - README.rdoc
72
+ files:
73
+ - .gitignore
74
+ - LICENSE
75
+ - README.rdoc
76
+ - Rakefile
77
+ - VERSION
78
+ - cdr-fetcher.gemspec
79
+ - lib/cdr-fetcher.rb
80
+ - test/helper.rb
81
+ - test/test_cdr-fetcher.rb
82
+ has_rdoc: true
83
+ homepage: http://github.com/chicks/cdr-fetcher
84
+ licenses: []
85
+
86
+ post_install_message:
87
+ rdoc_options:
88
+ - --charset=UTF-8
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ hash: 3
97
+ segments:
98
+ - 0
99
+ version: "0"
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ hash: 3
106
+ segments:
107
+ - 0
108
+ version: "0"
109
+ requirements: []
110
+
111
+ rubyforge_project:
112
+ rubygems_version: 1.3.7
113
+ signing_key:
114
+ specification_version: 3
115
+ summary: A gem for fetching CDR's from a remote system.
116
+ test_files:
117
+ - test/helper.rb
118
+ - test/test_cdr-fetcher.rb