jss-api 0.5.5 → 0.5.6
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.
- data/.yardopts +7 -0
- data/CHANGES.md +9 -1
- data/README.md +10 -5
- data/bin/cgrouper +485 -0
- data/bin/subnet-update +400 -0
- data/lib/jss-api/api_connection.rb +22 -11
- data/lib/jss-api/api_object.rb +9 -2
- data/lib/jss-api/api_object/locatable.rb +1 -1
- data/lib/jss-api/api_object/osx_configuration_profile.rb +261 -0
- data/lib/jss-api/api_object/self_servable.rb +355 -0
- data/lib/jss-api/configuration.rb +2 -0
- data/lib/jss-api/version.rb +1 -1
- metadata +25 -43
data/bin/subnet-update
ADDED
@@ -0,0 +1,400 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
### Copyright 2014 Pixar
|
4
|
+
###
|
5
|
+
### Licensed under the Apache License, Version 2.0 (the "Apache License")
|
6
|
+
### with the following modification; you may not use this file except in
|
7
|
+
### compliance with the Apache License and the following modification to it:
|
8
|
+
### Section 6. Trademarks. is deleted and replaced with:
|
9
|
+
###
|
10
|
+
### 6. Trademarks. This License does not grant permission to use the trade
|
11
|
+
### names, trademarks, service marks, or product names of the Licensor
|
12
|
+
### and its affiliates, except as required to comply with Section 4(c) of
|
13
|
+
### the License and to reproduce the content of the NOTICE file.
|
14
|
+
###
|
15
|
+
### You may obtain a copy of the Apache License at
|
16
|
+
###
|
17
|
+
### http://www.apache.org/licenses/LICENSE-2.0
|
18
|
+
###
|
19
|
+
### Unless required by applicable law or agreed to in writing, software
|
20
|
+
### distributed under the Apache License with the above modification is
|
21
|
+
### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
22
|
+
### KIND, either express or implied. See the Apache License for the specific
|
23
|
+
### language governing permissions and limitations under the Apache License.
|
24
|
+
|
25
|
+
##############################
|
26
|
+
# == Synopsis
|
27
|
+
# Add, remove, or change the Network Segments in the JSS based on data from an input file
|
28
|
+
# in CSV, tab, or other delimited format.
|
29
|
+
#
|
30
|
+
# == Usage
|
31
|
+
# subnet-update [-t | -d delimiter] [-h] file
|
32
|
+
#
|
33
|
+
#
|
34
|
+
# == Author
|
35
|
+
# Chris Lasell <chrisl@pixar.com>
|
36
|
+
#
|
37
|
+
# == Copyright
|
38
|
+
# Copyright (c) 2014 Pixar Animation Studios
|
39
|
+
|
40
|
+
##############################
|
41
|
+
# Libraries
|
42
|
+
require 'jss-api'
|
43
|
+
require 'getoptlong'
|
44
|
+
|
45
|
+
##############################
|
46
|
+
# The app object
|
47
|
+
class App
|
48
|
+
|
49
|
+
##############################
|
50
|
+
# Constants
|
51
|
+
|
52
|
+
USAGE = "Usage: #{File.basename($0)} [-d delim] [--header] [-c col1,col2,col3 ] [-m manual-prefix] [--help] /path/to/file"
|
53
|
+
|
54
|
+
POTENTIAL_COLUMNS = [:name, :starting, :ending, :cidr]
|
55
|
+
|
56
|
+
# Whenever we process a file, we store it here. The next time we
|
57
|
+
# run, if the input file is identical to this, we exit witout doing anything.
|
58
|
+
DEFAULT_CACHE_FILE = Pathname.new("~/.last_subnet_update").expand_path
|
59
|
+
|
60
|
+
DEFAULT_DELIMITER = "\t"
|
61
|
+
DEFAULT_COLUMNS = [:name, :starting, :ending]
|
62
|
+
DEFAULT_MANUAL_PREFIX = "Manual-"
|
63
|
+
|
64
|
+
|
65
|
+
attr_reader :debug
|
66
|
+
|
67
|
+
###############
|
68
|
+
# set up
|
69
|
+
def initialize(args)
|
70
|
+
|
71
|
+
# set defaults
|
72
|
+
@debug = false
|
73
|
+
|
74
|
+
@delim = DEFAULT_DELIMITER
|
75
|
+
@header = false
|
76
|
+
@columns = DEFAULT_COLUMNS
|
77
|
+
@cache_file = DEFAULT_CACHE_FILE
|
78
|
+
@manual_prefix = DEFAULT_MANUAL_PREFIX
|
79
|
+
|
80
|
+
|
81
|
+
#define the cli opts
|
82
|
+
cli_opts = GetoptLong.new(
|
83
|
+
[ '--help', '-H', GetoptLong::NO_ARGUMENT ],
|
84
|
+
[ '--delimiter', '--delim', '-d', GetoptLong::REQUIRED_ARGUMENT],
|
85
|
+
[ '--header', '-h', GetoptLong::NO_ARGUMENT],
|
86
|
+
[ '--columns', '-c', GetoptLong::OPTIONAL_ARGUMENT],
|
87
|
+
[ '--manual-prefix', '-m', GetoptLong::OPTIONAL_ARGUMENT],
|
88
|
+
[ '--cache', GetoptLong::REQUIRED_ARGUMENT ],
|
89
|
+
[ '--debug', GetoptLong::NO_ARGUMENT],
|
90
|
+
[ '--server', '-S', GetoptLong::OPTIONAL_ARGUMENT],
|
91
|
+
[ '--port', '-P', GetoptLong::OPTIONAL_ARGUMENT],
|
92
|
+
[ '--user', '-U', GetoptLong::OPTIONAL_ARGUMENT],
|
93
|
+
[ '--no-verify-cert', '-V', GetoptLong::NO_ARGUMENT],
|
94
|
+
[ '--timeout', '-T', GetoptLong::OPTIONAL_ARGUMENT]
|
95
|
+
)
|
96
|
+
|
97
|
+
# parse the cli opts
|
98
|
+
cli_opts.each do |opt, arg|
|
99
|
+
case opt
|
100
|
+
when '--help' then show_help
|
101
|
+
when '--delimiter' then @delim = arg
|
102
|
+
when '--header' then @header = true
|
103
|
+
when '--columns' then @columns = arg.split(',').map{|c| c.to_sym}
|
104
|
+
when '--manual-prefix' then @manual_prefix = arg
|
105
|
+
when '--cache' then @cache_file = Pathname.new arg
|
106
|
+
when '--debug' then @debug = true
|
107
|
+
when '--server'
|
108
|
+
@server = arg
|
109
|
+
|
110
|
+
when '--port'
|
111
|
+
@port = arg
|
112
|
+
|
113
|
+
when '--user'
|
114
|
+
@user = arg
|
115
|
+
|
116
|
+
when '--no-verify-cert'
|
117
|
+
@verify_cert = false
|
118
|
+
|
119
|
+
when '--timeout'
|
120
|
+
@timeout = arg
|
121
|
+
|
122
|
+
end # case
|
123
|
+
end # each opt arg
|
124
|
+
|
125
|
+
|
126
|
+
@columns = nil if @columns and @columns.empty?
|
127
|
+
|
128
|
+
@file = args.shift
|
129
|
+
|
130
|
+
|
131
|
+
end # init
|
132
|
+
|
133
|
+
###############
|
134
|
+
# Go!
|
135
|
+
def run
|
136
|
+
|
137
|
+
unless @file
|
138
|
+
puts "No input file specified."
|
139
|
+
puts USAGE
|
140
|
+
return
|
141
|
+
end
|
142
|
+
|
143
|
+
@file = Pathname.new @file
|
144
|
+
|
145
|
+
unless parse_file
|
146
|
+
puts "File hasn't changed since last time, no changes to make!"
|
147
|
+
return
|
148
|
+
end
|
149
|
+
|
150
|
+
# use any config settings defined....
|
151
|
+
@user ||= JSS::CONFIG.api_username
|
152
|
+
@server ||= JSS::CONFIG.api_server_name
|
153
|
+
@getpass = $stdin.tty? ? :prompt : :stdin
|
154
|
+
|
155
|
+
raise JSS::MissingDataError, "No JSS Username provided or found in the JSS gem config." unless @user
|
156
|
+
raise JSS::MissingDataError, "No JSS Server provided or found in the JSS gem config." unless @server
|
157
|
+
|
158
|
+
JSS::API.connect( :server => @server,
|
159
|
+
:port => @port,
|
160
|
+
:verify_cert => @verify_cert,
|
161
|
+
:user => @user,
|
162
|
+
:pw => @getpass,
|
163
|
+
:stdin_line => 1,
|
164
|
+
:timeout => @timeout
|
165
|
+
)
|
166
|
+
|
167
|
+
update_network_segments
|
168
|
+
|
169
|
+
end # run
|
170
|
+
|
171
|
+
#####################################
|
172
|
+
###
|
173
|
+
### Show Help
|
174
|
+
###
|
175
|
+
def show_help
|
176
|
+
puts <<-FULLHELP
|
177
|
+
Update the JSS Network Segments from a delimited file of subnet information.
|
178
|
+
|
179
|
+
#{USAGE}
|
180
|
+
|
181
|
+
Options:
|
182
|
+
-d, --delimiter - The field delimiter in the file, defaults to tab.
|
183
|
+
-c, --columns [col1,col2,col3]
|
184
|
+
- The column order in file, must include 'name', 'starting',
|
185
|
+
and either 'ending' or 'cidr'
|
186
|
+
-h, --header - The first line of the file is a header line,
|
187
|
+
possibly defining the columns
|
188
|
+
-m, --manual-prefix - Network Segment names in the JSS with this prefix are ignored.
|
189
|
+
Defaults to 'Manual-'
|
190
|
+
--cache /path/.. - Where read/save the input data for comparison between runs.
|
191
|
+
Defaults to ~/.last_subnet_update
|
192
|
+
-S, --server srvr - specify the JSS API server name
|
193
|
+
-P, --port portnum - specify the JSS API port
|
194
|
+
-U, --user username - specify the JSS API user
|
195
|
+
-V, --no-verify-cert - Allow self-signed, unverified SSL certificate
|
196
|
+
-T, --timeout secs - specify the JSS API timeout
|
197
|
+
-H, --help - show this help
|
198
|
+
--debug - show the ruby backtrace when errors occur
|
199
|
+
|
200
|
+
This program parses the input file line by line (possibly accounting for a header line).
|
201
|
+
Each line defines the name and IP-range of a subnet/network segment.
|
202
|
+
|
203
|
+
- If a segment doesn't exist in the JSS, it is created.
|
204
|
+
- If a segment's range has changed, it is updated in the JSS.
|
205
|
+
- If a JSS segment doesn't exist in the file, it is deleted from the JSS
|
206
|
+
unless its name starts with the --manual-prefix
|
207
|
+
|
208
|
+
Input File:
|
209
|
+
- The file must contain three columns, separated by the --delimiter,
|
210
|
+
with these names, in any order:
|
211
|
+
- 'name' (the network segment name)
|
212
|
+
- 'starting' (the starting IP address of the network segment)
|
213
|
+
- EITHER of:
|
214
|
+
- 'ending' (the ending IP address of the network segment)
|
215
|
+
- 'cidr' (the network range of the segment as a CIDR bitmask, e.g. '24')
|
216
|
+
Notes:
|
217
|
+
- The --columns option is a comma-separted list of the three
|
218
|
+
column names aboveindicating the column-order in the file.
|
219
|
+
|
220
|
+
- If --columns are not provided, and --header is specified, the first line
|
221
|
+
is assumed to contain the column names, separated by the delimiter
|
222
|
+
|
223
|
+
- If --header is provided with --columns, the first line of the file is ignored.
|
224
|
+
|
225
|
+
- The raw data from the file is cached and compared to the input file at
|
226
|
+
the next run. If the data is identical, no JSS connection is made.
|
227
|
+
|
228
|
+
- If no API settings are provided, they will be read from /etc/jss_gem.conf
|
229
|
+
and ~/.jss_gem.conf. See the JSS Gem docs for details.
|
230
|
+
|
231
|
+
- The password for the connection will be read from STDIN or prompted if needed
|
232
|
+
|
233
|
+
FULLHELP
|
234
|
+
exit 0
|
235
|
+
end
|
236
|
+
|
237
|
+
########################
|
238
|
+
# parse the incoming data file.
|
239
|
+
# If the file hasn't changed from the last time we processed it
|
240
|
+
# then return false
|
241
|
+
# otherwise parse it into @parsed_data and return true
|
242
|
+
# @parsed_data is an array of hashes, each with :name, :starting, and :ending
|
243
|
+
#
|
244
|
+
def parse_file
|
245
|
+
|
246
|
+
raise "'#{@file}' is not readable, or not a regular file" unless @file.readable? and @file.file?
|
247
|
+
|
248
|
+
# read in the file
|
249
|
+
@raw_data = @file.read
|
250
|
+
|
251
|
+
# compare it to the one we used last time
|
252
|
+
if @cache_file.readable?
|
253
|
+
return false if @raw_data == @cache_file.read
|
254
|
+
end
|
255
|
+
|
256
|
+
# split the data into an array by newlines
|
257
|
+
lines = @raw_data.split "\n"
|
258
|
+
|
259
|
+
# remove the first line if its a header, and parse it into the columns
|
260
|
+
# if needed
|
261
|
+
if @header
|
262
|
+
header = lines.shift
|
263
|
+
@columns ||= header.split(/\s*#{@delim}\s*/).map{|c| c.to_sym}
|
264
|
+
end
|
265
|
+
|
266
|
+
# check some state
|
267
|
+
raise "Columns must include 'name' and 'starting'" unless @columns.include?(:name) and @columns.include?(:starting)
|
268
|
+
raise "Columns must include either 'ending' or 'cidr'" unless @columns.include?(:ending) or @columns.include?(:cidr)
|
269
|
+
|
270
|
+
@use_cidr = @columns.include? :cidr
|
271
|
+
|
272
|
+
|
273
|
+
# which columns are which in the file?
|
274
|
+
name = @columns.index :name
|
275
|
+
starting = @columns.index :starting
|
276
|
+
ending = @use_cidr ? @columns.index(:cidr) : @columns.index(:ending)
|
277
|
+
|
278
|
+
# split each line and convert it into a hash
|
279
|
+
@parsed_data = lines.map do |line|
|
280
|
+
|
281
|
+
parts = line.split(@delim).map{|f| f.strip }
|
282
|
+
|
283
|
+
unless parts[name] and parts[starting] and parts[ending]
|
284
|
+
puts "Skipping invalid line: #{line}"
|
285
|
+
next
|
286
|
+
end
|
287
|
+
|
288
|
+
|
289
|
+
{:name => parts[name], :starting => parts[starting], :ending => parts[ending]}
|
290
|
+
end
|
291
|
+
|
292
|
+
# parsed data is now an array of hashes
|
293
|
+
return true
|
294
|
+
end
|
295
|
+
|
296
|
+
#############################################
|
297
|
+
#############################################
|
298
|
+
# Update the JSS Network Segments from GIT_NETBLOCKS_URL, q.v.
|
299
|
+
def update_network_segments
|
300
|
+
|
301
|
+
# CREATE any that are in the parsed data but not yet in the JSS,
|
302
|
+
# and UPDATE any that exist but have modified ranges.
|
303
|
+
# While looping through, make a hash of JSS::NetworkSegment objects, keyed by their name.
|
304
|
+
segs_from_data = {}
|
305
|
+
|
306
|
+
@parsed_data.each do |pd|
|
307
|
+
|
308
|
+
# skip anthing with the manual prefix
|
309
|
+
next if pd[:name].start_with? @manual_prefix
|
310
|
+
|
311
|
+
ender = @use_cidr ? :cidr : :ending_address
|
312
|
+
|
313
|
+
begin
|
314
|
+
this_seg = JSS::NetworkSegment.new(:id => :new, :name => pd[:name], :starting_address => pd[:starting], ender => pd[:ending])
|
315
|
+
|
316
|
+
# If the new netsegment should have other settings (dist. point, netboot server, etc...)
|
317
|
+
# here's where you should apply those settings.
|
318
|
+
|
319
|
+
this_seg.create
|
320
|
+
puts "Added Network Segment '#{this_seg.name}' to the JSS"
|
321
|
+
|
322
|
+
# it already exists, so see if it needs any changes
|
323
|
+
rescue JSS::AlreadyExistsError
|
324
|
+
|
325
|
+
# there's already one with this name, so just grab it.
|
326
|
+
this_seg = JSS::NetworkSegment.new( :name => pd[:name])
|
327
|
+
|
328
|
+
# does the startng addres need to be changed?
|
329
|
+
needs_update = this_seg.starting_address.to_s != pd[:starting].to_s
|
330
|
+
|
331
|
+
# even if we don't need to update the starting, we might need to update
|
332
|
+
# the ending...
|
333
|
+
unless needs_update
|
334
|
+
if @use_cidr
|
335
|
+
needs_update = this_seg.cidr.to_i != pd[:ending].to_i
|
336
|
+
else
|
337
|
+
needs_update = this_seg.ending_address.to_s != pd[:ending].to_s
|
338
|
+
end # if @use_cidr
|
339
|
+
end #unless needs update
|
340
|
+
|
341
|
+
# did we decide we need an update?
|
342
|
+
if needs_update
|
343
|
+
this_seg.starting_address = pd[:starting]
|
344
|
+
if @use_cidr
|
345
|
+
this_seg.cidr = pd[:ending].to_i
|
346
|
+
else
|
347
|
+
this_seg.ending_address = pd[:ending]
|
348
|
+
end # if @use_cidr
|
349
|
+
this_seg.update
|
350
|
+
puts "Updated IP range for Network Segment '#{this_seg.name}'"
|
351
|
+
|
352
|
+
else # doesn't need update
|
353
|
+
puts "Network Segment '#{this_seg.name}' doesn't have any changes."
|
354
|
+
end # if needs update
|
355
|
+
|
356
|
+
# rescue other errors
|
357
|
+
rescue
|
358
|
+
raise "There was an error with NetworkSegment #{pd[:name]}: #{$!}"
|
359
|
+
end # begin
|
360
|
+
|
361
|
+
segs_from_data[this_seg.name] = this_seg
|
362
|
+
end
|
363
|
+
|
364
|
+
|
365
|
+
# DELETE those in jss, but not in parsed data,
|
366
|
+
# unless the name starts with @manual_prefix
|
367
|
+
JSS::NetworkSegment.map_all_ids_to(:name).each do |id,name|
|
368
|
+
|
369
|
+
next if name.start_with? @manual_prefix
|
370
|
+
|
371
|
+
unless segs_from_data.keys.include? name
|
372
|
+
JSS::NetworkSegment.new(:id => id).delete
|
373
|
+
puts "Deleted Network Segment '#{name}' from the JSS"
|
374
|
+
end # unless
|
375
|
+
|
376
|
+
end # jss_uids.each seg
|
377
|
+
|
378
|
+
# save the data into a file for comparison next time
|
379
|
+
@cache_file.jss_save @raw_data
|
380
|
+
|
381
|
+
# all done
|
382
|
+
return true
|
383
|
+
end # update_network_segments
|
384
|
+
|
385
|
+
end # app
|
386
|
+
|
387
|
+
##############################
|
388
|
+
# create the app and go
|
389
|
+
begin
|
390
|
+
app = App.new(ARGV)
|
391
|
+
app.run
|
392
|
+
rescue
|
393
|
+
# handle exceptions not handled elsewhere
|
394
|
+
puts "An error occurred: #{$!}"
|
395
|
+
puts "Backtrace:" if app.debug
|
396
|
+
puts $@ if app.debug
|
397
|
+
|
398
|
+
ensure
|
399
|
+
|
400
|
+
end
|
@@ -77,7 +77,10 @@ module JSS
|
|
77
77
|
### Default timeouts in seconds
|
78
78
|
DFT_OPEN_TIMEOUT = 60
|
79
79
|
DFT_TIMEOUT = 60
|
80
|
-
|
80
|
+
|
81
|
+
### The Default SSL Version
|
82
|
+
DFT_SSL_VERSION = 'TLSv1'
|
83
|
+
|
81
84
|
#####################################
|
82
85
|
### Attributes
|
83
86
|
#####################################
|
@@ -115,15 +118,15 @@ module JSS
|
|
115
118
|
###
|
116
119
|
### @param args[Hash] the keyed arguments for connection.
|
117
120
|
###
|
118
|
-
### @option args :server[String]
|
121
|
+
### @option args :server[String] the hostname of the JSS API server, required if not defined in JSS::CONFIG
|
119
122
|
###
|
120
123
|
### @option args :port[Integer] the port number to connect with, defaults to 8443
|
121
124
|
###
|
122
|
-
### @option args :verify_cert[Boolean]should HTTPS SSL certificates be verified. Defaults to true.
|
125
|
+
### @option args :verify_cert[Boolean] should HTTPS SSL certificates be verified. Defaults to true.
|
123
126
|
### If your connection raises RestClient::SSLCertificateNotVerified, and you don't care about the
|
124
127
|
### validity of the SSL cert. just set this explicitly to false.
|
125
128
|
###
|
126
|
-
### @option args :user[String]
|
129
|
+
### @option args :user[String] a JSS user who has API privs, required if not defined in JSS::CONFIG
|
127
130
|
###
|
128
131
|
### @option args :pw[String,Symbol] Required, the password for that user, or :prompt, or :stdin
|
129
132
|
### If :prompt, the user is promted on the commandline to enter the password for the :user.
|
@@ -145,6 +148,7 @@ module JSS
|
|
145
148
|
args[:user] ||= JSS::CONFIG.api_username
|
146
149
|
args[:timeout] ||= JSS::CONFIG.api_timeout
|
147
150
|
args[:open_timeout] ||= JSS::CONFIG.api_timeout_open
|
151
|
+
args[:ssl_version] ||= JSS::CONFIG.api_ssl_version
|
148
152
|
|
149
153
|
# if verify cert given was NOT in the args....
|
150
154
|
if args[:verify_cert].nil?
|
@@ -156,12 +160,20 @@ module JSS
|
|
156
160
|
args[:port] ||= SSL_PORT
|
157
161
|
args[:timeout] ||= DFT_TIMEOUT
|
158
162
|
args[:open_timeout] ||= DFT_OPEN_TIMEOUT
|
159
|
-
|
163
|
+
|
164
|
+
# As of Casper 9.61 we can't use SSL, must use TLS, since SSLv3 was susceptible to poodles.
|
165
|
+
# NOTE - this requires rest-client v 1.7.0 or higher
|
166
|
+
# which requires mime-types 2.0 or higher, which requires ruby 1.9.2 or higher!
|
167
|
+
# That means that support for ruby 1.8.7 stops with Casper 9.6
|
168
|
+
args[:ssl_version] ||= DFT_SSL_VERSION
|
169
|
+
|
170
|
+
|
160
171
|
# must have server, user, and pw
|
161
|
-
raise JSS::MissingDataError, "
|
162
|
-
raise JSS::MissingDataError, "
|
172
|
+
raise JSS::MissingDataError, "No JSS :server specified, or in configuration." unless args[:server]
|
173
|
+
raise JSS::MissingDataError, "No JSS :user specified, or in configuration." unless args[:user]
|
163
174
|
raise JSS::MissingDataError, "Missing :pw for user '#{args[:user]}'" unless args[:pw]
|
164
|
-
|
175
|
+
|
176
|
+
# ssl or not?
|
165
177
|
ssl = SSL_PORT == args[:port].to_i ? "s" : ''
|
166
178
|
@rest_url = URI::encode "http#{ssl}://#{args[:server]}:#{args[:port]}/#{RSRC}"
|
167
179
|
|
@@ -169,9 +181,6 @@ module JSS
|
|
169
181
|
# if verify_cert is nil (unset) or non-false, then we will verify
|
170
182
|
args[:verify_ssl] = (args[:verify_cert].nil? or args[:verify_cert]) ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
171
183
|
|
172
|
-
# make sure we have a user
|
173
|
-
raise JSS::MissingDataError, "No JSS user specified, or listed in configuration." unless args[:user]
|
174
|
-
|
175
184
|
args[:password] = if args[:pw] == :prompt
|
176
185
|
JSS.prompt_for_password "Enter the password for JSS user '#{args[:user]}':"
|
177
186
|
elsif args[:pw].is_a?(Symbol) and args[:pw].to_s.start_with?('stdin')
|
@@ -183,6 +192,8 @@ module JSS
|
|
183
192
|
args[:pw]
|
184
193
|
end
|
185
194
|
|
195
|
+
|
196
|
+
|
186
197
|
# heres our connection
|
187
198
|
@cnx = RestClient::Resource.new("#{@rest_url}", args)
|
188
199
|
|