MuranoCLI 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +28 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +21 -0
  5. data/Gemfile +27 -0
  6. data/LICENSE.txt +19 -0
  7. data/MuranoCLI.gemspec +50 -0
  8. data/MuranoCLI.iss +50 -0
  9. data/README.markdown +208 -0
  10. data/Rakefile +188 -0
  11. data/TODO.taskpaper +122 -0
  12. data/bin/mr +8 -0
  13. data/bin/murano +84 -0
  14. data/docs/demo.md +109 -0
  15. data/lib/MrMurano/Account.rb +211 -0
  16. data/lib/MrMurano/Config-Migrate.rb +47 -0
  17. data/lib/MrMurano/Config.rb +286 -0
  18. data/lib/MrMurano/Mock.rb +63 -0
  19. data/lib/MrMurano/Product-1P-Device.rb +145 -0
  20. data/lib/MrMurano/Product-Resources.rb +195 -0
  21. data/lib/MrMurano/Product.rb +358 -0
  22. data/lib/MrMurano/ProjectFile.rb +349 -0
  23. data/lib/MrMurano/Solution-Cors.rb +46 -0
  24. data/lib/MrMurano/Solution-Endpoint.rb +177 -0
  25. data/lib/MrMurano/Solution-File.rb +150 -0
  26. data/lib/MrMurano/Solution-ServiceConfig.rb +140 -0
  27. data/lib/MrMurano/Solution-Services.rb +326 -0
  28. data/lib/MrMurano/Solution-Users.rb +129 -0
  29. data/lib/MrMurano/Solution.rb +59 -0
  30. data/lib/MrMurano/SubCmdGroupContext.rb +49 -0
  31. data/lib/MrMurano/SyncUpDown.rb +565 -0
  32. data/lib/MrMurano/commands/assign.rb +57 -0
  33. data/lib/MrMurano/commands/businessList.rb +45 -0
  34. data/lib/MrMurano/commands/completion.rb +152 -0
  35. data/lib/MrMurano/commands/config.rb +67 -0
  36. data/lib/MrMurano/commands/content.rb +130 -0
  37. data/lib/MrMurano/commands/cors.rb +30 -0
  38. data/lib/MrMurano/commands/domain.rb +17 -0
  39. data/lib/MrMurano/commands/gb.rb +33 -0
  40. data/lib/MrMurano/commands/init.rb +138 -0
  41. data/lib/MrMurano/commands/keystore.rb +157 -0
  42. data/lib/MrMurano/commands/logs.rb +78 -0
  43. data/lib/MrMurano/commands/mock.rb +63 -0
  44. data/lib/MrMurano/commands/password.rb +88 -0
  45. data/lib/MrMurano/commands/postgresql.rb +41 -0
  46. data/lib/MrMurano/commands/product.rb +14 -0
  47. data/lib/MrMurano/commands/productCreate.rb +39 -0
  48. data/lib/MrMurano/commands/productDelete.rb +33 -0
  49. data/lib/MrMurano/commands/productDevice.rb +84 -0
  50. data/lib/MrMurano/commands/productDeviceIdCmds.rb +86 -0
  51. data/lib/MrMurano/commands/productList.rb +45 -0
  52. data/lib/MrMurano/commands/productWrite.rb +27 -0
  53. data/lib/MrMurano/commands/show.rb +80 -0
  54. data/lib/MrMurano/commands/solution.rb +14 -0
  55. data/lib/MrMurano/commands/solutionCreate.rb +39 -0
  56. data/lib/MrMurano/commands/solutionDelete.rb +34 -0
  57. data/lib/MrMurano/commands/solutionList.rb +45 -0
  58. data/lib/MrMurano/commands/status.rb +92 -0
  59. data/lib/MrMurano/commands/sync.rb +60 -0
  60. data/lib/MrMurano/commands/timeseries.rb +115 -0
  61. data/lib/MrMurano/commands/tsdb.rb +271 -0
  62. data/lib/MrMurano/commands/usage.rb +23 -0
  63. data/lib/MrMurano/commands/zshcomplete.erb +112 -0
  64. data/lib/MrMurano/commands.rb +32 -0
  65. data/lib/MrMurano/hash.rb +20 -0
  66. data/lib/MrMurano/http.rb +153 -0
  67. data/lib/MrMurano/makePretty.rb +75 -0
  68. data/lib/MrMurano/schema/pf-v1.0.0.yaml +114 -0
  69. data/lib/MrMurano/schema/sf-v0.2.0.yaml +77 -0
  70. data/lib/MrMurano/schema/sf-v0.3.0.yaml +78 -0
  71. data/lib/MrMurano/template/mock.erb +9 -0
  72. data/lib/MrMurano/template/projectFile.murano.erb +81 -0
  73. data/lib/MrMurano/verbosing.rb +99 -0
  74. data/lib/MrMurano/version.rb +4 -0
  75. data/lib/MrMurano.rb +20 -0
  76. data/spec/Account-Passwords_spec.rb +242 -0
  77. data/spec/Account_spec.rb +272 -0
  78. data/spec/ConfigFile_spec.rb +50 -0
  79. data/spec/ConfigMigrate_spec.rb +89 -0
  80. data/spec/Config_spec.rb +409 -0
  81. data/spec/Http_spec.rb +204 -0
  82. data/spec/MakePretties_spec.rb +118 -0
  83. data/spec/Mock_spec.rb +53 -0
  84. data/spec/ProductBase_spec.rb +113 -0
  85. data/spec/ProductContent_spec.rb +162 -0
  86. data/spec/ProductResources_spec.rb +329 -0
  87. data/spec/Product_1P_Device_spec.rb +202 -0
  88. data/spec/Product_1P_RPC_spec.rb +175 -0
  89. data/spec/Product_spec.rb +153 -0
  90. data/spec/ProjectFile_spec.rb +324 -0
  91. data/spec/Solution-Cors_spec.rb +164 -0
  92. data/spec/Solution-Endpoint_spec.rb +581 -0
  93. data/spec/Solution-File_spec.rb +212 -0
  94. data/spec/Solution-ServiceConfig_spec.rb +202 -0
  95. data/spec/Solution-ServiceDevice_spec.rb +176 -0
  96. data/spec/Solution-ServiceEventHandler_spec.rb +385 -0
  97. data/spec/Solution-ServiceModules_spec.rb +465 -0
  98. data/spec/Solution-UsersRoles_spec.rb +207 -0
  99. data/spec/Solution_spec.rb +92 -0
  100. data/spec/SyncRoot_spec.rb +83 -0
  101. data/spec/SyncUpDown_spec.rb +495 -0
  102. data/spec/Verbosing_spec.rb +279 -0
  103. data/spec/_workspace.rb +27 -0
  104. data/spec/cmd_assign_spec.rb +51 -0
  105. data/spec/cmd_business_spec.rb +59 -0
  106. data/spec/cmd_common.rb +72 -0
  107. data/spec/cmd_config_spec.rb +68 -0
  108. data/spec/cmd_content_spec.rb +71 -0
  109. data/spec/cmd_cors_spec.rb +50 -0
  110. data/spec/cmd_device_spec.rb +96 -0
  111. data/spec/cmd_domain_spec.rb +32 -0
  112. data/spec/cmd_init_spec.rb +30 -0
  113. data/spec/cmd_keystore_spec.rb +97 -0
  114. data/spec/cmd_password_spec.rb +62 -0
  115. data/spec/cmd_status_spec.rb +239 -0
  116. data/spec/cmd_syncdown_spec.rb +86 -0
  117. data/spec/cmd_syncup_spec.rb +62 -0
  118. data/spec/cmd_usage_spec.rb +36 -0
  119. data/spec/fixtures/.mrmuranorc +9 -0
  120. data/spec/fixtures/ProjectFiles/invalid.yaml +9 -0
  121. data/spec/fixtures/ProjectFiles/only_meta.yaml +24 -0
  122. data/spec/fixtures/ProjectFiles/with_routes.yaml +27 -0
  123. data/spec/fixtures/SolutionFiles/0.2.0.json +20 -0
  124. data/spec/fixtures/SolutionFiles/0.2.0_invalid.json +18 -0
  125. data/spec/fixtures/SolutionFiles/0.2.json +21 -0
  126. data/spec/fixtures/SolutionFiles/0.3.0.json +20 -0
  127. data/spec/fixtures/SolutionFiles/0.3.0_invalid.json +19 -0
  128. data/spec/fixtures/SolutionFiles/0.3.json +20 -0
  129. data/spec/fixtures/SolutionFiles/basic.json +20 -0
  130. data/spec/fixtures/SolutionFiles/secret.json +6 -0
  131. data/spec/fixtures/configfile +9 -0
  132. data/spec/fixtures/dumped_config +42 -0
  133. data/spec/fixtures/mrmuranorc_deleted_bob +8 -0
  134. data/spec/fixtures/mrmuranorc_tool_bob +3 -0
  135. data/spec/fixtures/product_spec_files/example.exoline.spec.yaml +116 -0
  136. data/spec/fixtures/product_spec_files/example.murano.spec.yaml +14 -0
  137. data/spec/fixtures/product_spec_files/gwe.exoline.spec.yaml +21 -0
  138. data/spec/fixtures/product_spec_files/gwe.murano.spec.yaml +16 -0
  139. data/spec/fixtures/product_spec_files/lightbulb-no-state.yaml +11 -0
  140. data/spec/fixtures/product_spec_files/lightbulb.yaml +14 -0
  141. data/spec/fixtures/roles-three.yaml +11 -0
  142. data/spec/fixtures/syncable_content/assets/icon.png +0 -0
  143. data/spec/fixtures/syncable_content/assets/index.html +0 -0
  144. data/spec/fixtures/syncable_content/assets/js/script.js +0 -0
  145. data/spec/fixtures/syncable_content/modules/table_util.lua +58 -0
  146. data/spec/fixtures/syncable_content/routes/manyRoutes.lua +11 -0
  147. data/spec/fixtures/syncable_content/routes/singleRoute.lua +5 -0
  148. data/spec/fixtures/syncable_content/services/devdata.lua +18 -0
  149. data/spec/fixtures/syncable_content/services/timers.lua +4 -0
  150. data/spec/spec_helper.rb +119 -0
  151. metadata +498 -0
@@ -0,0 +1,195 @@
1
+ require 'MrMurano/Product'
2
+ require 'MrMurano/SyncUpDown'
3
+
4
+ module MrMurano
5
+
6
+ ## Manage the resources on a Product
7
+ #
8
+ # There isn't an okami-shim for most of this, it maps right over to 1P-RPC.
9
+ # Or better stated, that's all okami-shim is.
10
+ class ProductResources < ProductBase
11
+ include SyncUpDown
12
+ include ProductOnePlatformRpcShim
13
+
14
+ def initialize
15
+ super
16
+ @uriparts << :proxy
17
+ @uriparts << 'onep:v1'
18
+ @uriparts << :rpc
19
+ @uriparts << :process
20
+ @model_rid = nil
21
+
22
+ @itemkey = :rid # this is the key that is the identifier used by murano
23
+ end
24
+
25
+ ## Get 1P info about the prodcut
26
+ def info
27
+ do_rpc({:id=>1,
28
+ :procedure=>:info,
29
+ :arguments=>[model_rid, {}]
30
+ })
31
+ end
32
+
33
+ ## Return a list of the product resources as items
34
+ def list()
35
+ data = info()
36
+ ret = []
37
+ data[:aliases].each do |rid, aliases|
38
+ aliases.each do |al|
39
+ ret << {
40
+ :alias => al,
41
+ :rid => rid
42
+ }
43
+ end
44
+ end
45
+
46
+ ret
47
+ end
48
+
49
+ ## Fetch data from one resource
50
+ def fetch(rid)
51
+ do_rpc({:id=>1,
52
+ :procedure=>:info,
53
+ :arguments=>[rid, {}],
54
+ })
55
+ end
56
+
57
+ ## Remove a resource by RID
58
+ def remove(rid)
59
+ do_rpc({:id=>1,
60
+ :procedure=>:drop,
61
+ :arguments=>[rid]
62
+ })
63
+ end
64
+
65
+ ## Create a new resource in the prodcut
66
+ def create(alias_id, format=:string)
67
+ raise "Alias cannot be nil" if alias_id.nil?
68
+ # create then map.
69
+ rid = do_rpc({:id=>1,
70
+ :procedure=>:create,
71
+ :arguments=>[:dataport,
72
+ {:format=>format,
73
+ :name=>alias_id,
74
+ :retention=>{:count=>1,:duration=>:infinity}
75
+ }
76
+ ]
77
+ })
78
+ return rid unless not rid.kind_of?(String) or rid.match(/\p{XDigit}{40}/)
79
+
80
+ do_rpc({:id=>1,
81
+ :procedure=>:map,
82
+ :arguments=>[:alias, rid, alias_id]
83
+ })
84
+ end
85
+
86
+ ## Upload a resource.
87
+ # this is for SyncUpDown
88
+ # @param modify Bool: True if item exists already and this is changing it
89
+ def upload(src, item, modify)
90
+ if modify then
91
+ # this is usually a format change, which can only be set on create.
92
+ # So delete then create.
93
+ remove(item[:rid])
94
+ end
95
+ r = create(item[:alias], item[:format])
96
+ raise "Create Failed: #{r}" unless r.nil?
97
+ end
98
+
99
+ ## Use alias for doing sync compares
100
+ # (The RID will change if destroyed and recreated.)
101
+ def synckey(item)
102
+ item[:alias]
103
+ end
104
+
105
+ ##
106
+ #
107
+ def tolocalpath(into, item)
108
+ into
109
+ end
110
+
111
+ ## Get a local list of items from the single file
112
+ def localitems(from)
113
+ from = Pathname.new(from) unless from.kind_of? Pathname
114
+ debug "#{self.class.to_s}: Getting local items from: #{from}"
115
+ if not from.exist? then
116
+ warning "Skipping missing #{from.to_s}"
117
+ return []
118
+ end
119
+ unless from.file? then
120
+ warning "Cannot read from #{from.to_s}"
121
+ return []
122
+ end
123
+
124
+ here = []
125
+ from.open {|io| here = YAML.load(io) }
126
+ return [] if here == false
127
+
128
+ if here.kind_of?(Hash) and here.has_key?('resources') then
129
+ here['resources'].map{|i| Hash.transform_keys_to_symbols(i)}
130
+ else
131
+ warning "Unexpected data in #{from.to_s}"
132
+ []
133
+ end
134
+ end
135
+
136
+ def download(local, item)
137
+ # needs to append/merge with file
138
+ # for now, we'll read, modify, write
139
+ data = fetch(item[:rid])
140
+ item[:format] = data[:description][:format]
141
+
142
+ here = []
143
+ if local.exist? then
144
+ local.open('rb') {|io| here = YAML.load(io)}
145
+ here = [] if here == false
146
+ if here.kind_of?(Hash) and here.has_key?('resources') then
147
+ here = here['resources'].map{|i| Hash.transform_keys_to_symbols(i)}
148
+ else
149
+ here = []
150
+ end
151
+ end
152
+ here.delete_if do |i|
153
+ i[:alias] == item[:alias]
154
+ end
155
+ here << item.reject{|k,v| k==:synckey or k==:rid}
156
+ here.map!{|i| Hash.transform_keys_to_strings(i)}
157
+ local.dirname.mkpath
158
+ local.open('wb') do |io|
159
+ io << {'resources'=>here}.to_yaml
160
+ end
161
+ end
162
+
163
+ def removelocal(dest, item)
164
+ # needs to append/merge with file
165
+ # for now, we'll read, modify, write
166
+ here = []
167
+ if dest.exist? then
168
+ dest.open('rb') {|io| here = YAML.load(io)}
169
+ here = [] if here == false
170
+ if here.kind_of?(Hash) and here.has_key?('resources') then
171
+ here = here['resources'].map{|i| Hash.transform_keys_to_symbols(i)}
172
+ else
173
+ here = []
174
+ end
175
+ end
176
+ here.delete_if do |it|
177
+ it[:alias] == item[:alias]
178
+ end
179
+ here.map!{|i| Hash.transform_keys_to_strings(i)}
180
+ dest.open('wb') do|io|
181
+ io << {'resources'=>here}.to_yaml
182
+ end
183
+ end
184
+
185
+ ##
186
+ # True if itemA and itemB are different
187
+ def docmp(itemA, itemB)
188
+ itemA[:alias] != itemB[:alias] or itemA[:format] != itemB[:format]
189
+ end
190
+
191
+ end
192
+ SyncRoot.add('specs', ProductResources, 'P', %{Product Specification})
193
+
194
+ end
195
+ # vim: set ai et sw=2 ts=2 :
@@ -0,0 +1,358 @@
1
+ require 'uri'
2
+ require 'mime/types'
3
+ require 'csv'
4
+ require 'pp'
5
+ require 'MrMurano/http'
6
+ require 'MrMurano/verbosing'
7
+
8
+ module MrMurano
9
+ class ProductBase
10
+ def initialize
11
+ @pid = $cfg['product.id']
12
+ raise "No Product ID!" if @pid.nil?
13
+ @uriparts = [:product, @pid]
14
+ @project_section = :resources
15
+ end
16
+
17
+ include Http
18
+ include Verbose
19
+
20
+ ## Generate an endpoint in Murano
21
+ # Uses the uriparts and path
22
+ # @param path String: any additional parts for the URI
23
+ # @return URI: The full URI for this enpoint.
24
+ def endPoint(path='')
25
+ parts = ['https:/', $cfg['net.host'], 'api:1'] + @uriparts
26
+ s = parts.map{|v| v.to_s}.join('/')
27
+ URI(s + path.to_s)
28
+ end
29
+ end
30
+
31
+ module ProductOnePlatformRpcShim
32
+ ## The model RID for this product.
33
+ def model_rid
34
+ return @model_rid unless @model_rid.nil?
35
+ prd = Product.new
36
+ data = prd.info
37
+ if data.kind_of?(Hash) and data.has_key?(:modelrid) then
38
+ @model_rid = data[:modelrid]
39
+ else
40
+ raise "Bad info; #{data}"
41
+ end
42
+ @model_rid
43
+ end
44
+
45
+ ## Do a 1P RPC call
46
+ #
47
+ # While this will take an array of calls, don't. Only pass one.
48
+ # This only returns the result of the first call. Results from other calls are
49
+ # dropped.
50
+ def do_rpc(calls, cid=model_rid)
51
+ calls = [calls] unless calls.kind_of?(Array)
52
+ r = do_mrpc(calls, cid)
53
+ return r if not r.kind_of?(Array) or r.count < 1
54
+ r = r[0]
55
+ return r if not r.kind_of?(Hash) or r[:status] != 'ok'
56
+ r[:result]
57
+ end
58
+ private :do_rpc
59
+
60
+ ## Do many 1P RPC calls
61
+ def do_mrpc(calls, cid=model_rid)
62
+ calls = [calls] unless calls.kind_of?(Array)
63
+ maxid = ((calls.max_by{|c| c[:id] or 0 }[:id]) or 0)
64
+ calls.map!{|c| c[:id] = (maxid += 1) unless c.has_key?(:id); c}
65
+ post('', {
66
+ :auth=>(cid ? {:client_id=>cid} : {}),
67
+ :calls=>calls
68
+ })
69
+ end
70
+ private :do_mrpc
71
+
72
+ end
73
+
74
+ class Product < ProductBase
75
+ ## Get info about the product
76
+ def info
77
+ get('/info')
78
+ end
79
+
80
+ ## List enabled devices
81
+ def list(offset=0, limit=50)
82
+ get("/device/?offset=#{offset}&limit=#{limit}")
83
+ end
84
+
85
+ ## Enable a serial number
86
+ # This creates the device and opens the activation window.
87
+ def enable(sn)
88
+ post("/device/#{sn.to_s}")
89
+ end
90
+
91
+ ## Upload a spec file.
92
+ #
93
+ # Note that this will fail if any of the resources already exist.
94
+ def update(specFile)
95
+ specFile = Pathname.new(specFile) unless specFile.kind_of? Pathname
96
+
97
+ uri = endPoint('/definition')
98
+ request = Net::HTTP::Post.new(uri)
99
+ ret = nil
100
+
101
+ specFile.open do |io|
102
+ request.body_stream = io
103
+ request.content_length = specFile.size
104
+ set_def_headers(request)
105
+ request.content_type = 'text/yaml'
106
+ ret = workit(request)
107
+ end
108
+ ret
109
+ end
110
+
111
+ ## Write a value to an alias on a device
112
+ def write(sn, values)
113
+ post("/write/#{sn}", values) unless $cfg['tool.dry']
114
+ end
115
+
116
+ ## Converts an exoline style spec file into a Murano style one
117
+ # @param fin IO: IO Stream to read from
118
+ # @return String: Converted yaml data
119
+ def convertit(fin)
120
+ specOut = {'resources'=>[]}
121
+ spec = YAML.load(fin)
122
+ if spec.has_key?('dataports') and spec['dataports'].kind_of?(Array) then
123
+ dps = spec['dataports'].map do |dp|
124
+ dp.delete_if{|k,v| k != 'alias' and k != 'format' and k != 'initial'}
125
+ dp['format'] = 'string' if (dp['format']||'')[0..5] == 'string'
126
+ dp
127
+ end
128
+ specOut['resources'] = dps
129
+ else
130
+ raise "No dataports section found, or not an array"
131
+ end
132
+ specOut
133
+ end
134
+
135
+ ## Converts an exoline style spec file into a Murano style one
136
+ # @param specFile String: Path to file or '-' for stdin
137
+ # @return String: Converted yaml data
138
+ def convert(specFile)
139
+ if specFile == '-' then
140
+ convertit($stdin).to_yaml
141
+ else
142
+ specFile = Pathname.new(specFile) unless specFile.kind_of? Pathname
143
+ out = ''
144
+ specFile.open() do |fin|
145
+ out = convertit(fin).to_yaml
146
+ end
147
+ out
148
+ end
149
+ end
150
+ end
151
+
152
+ ##
153
+ # Manage the uploadable content for products.
154
+ class ProductContent < ProductBase
155
+ def initialize
156
+ super
157
+ @uriparts << :proxy
158
+ @uriparts << :provision
159
+ @uriparts << :manage
160
+ @uriparts << :content
161
+ @uriparts << @pid
162
+ end
163
+
164
+ ## List all things in content area
165
+ def list
166
+ ret = get('/')
167
+ return [] if ret.kind_of?(Hash)
168
+ ret.lines.map{|i|i.chomp}
169
+ end
170
+
171
+ ## List all contents allowed for sn
172
+ def list_for(sn)
173
+ ret = get("/?sn=#{sn}")
174
+ return [] if ret.kind_of?(Hash)
175
+ ret.lines.map{|i|i.chomp}
176
+ end
177
+
178
+ ## Create a new content item
179
+ def create(id, meta='', protect=false)
180
+ http_reset
181
+ data = {:id=>id, :meta=>meta}
182
+ data[:protected] = true if protect
183
+ postf('/', data)
184
+ end
185
+
186
+ ## Remove Content item
187
+ def remove(id)
188
+ postf('/', {:id=>id, :delete=>true})
189
+ end
190
+
191
+ ## Get info for content item
192
+ def info(id)
193
+ get("/#{id}") do |request, http|
194
+ http.request(request) do |resp|
195
+ case resp
196
+ when Net::HTTPSuccess
197
+ return CSV.parse(resp.body)
198
+ else
199
+ return nil
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ ## Download data for content item
206
+ def download(id, &block)
207
+ get("/#{id}?download=true") do |request, http|
208
+ http.request(request) do |resp|
209
+ case resp
210
+ when Net::HTTPSuccess
211
+ if block_given? then
212
+ resp.read_body(&block)
213
+ else
214
+ resp.read_body do |chunk|
215
+ $stdout.write chunk
216
+ end
217
+ end
218
+ else
219
+ showHttpError(request, resp)
220
+ end
221
+ end
222
+ nil
223
+ end
224
+ end
225
+
226
+ ## Upload data for content item
227
+ # TODO: add support for passing in IOStream
228
+ # @param modify Bool: True if item exists already and this is changing it
229
+ def upload(id, path, modify=false)
230
+ path = Pathname.new(path) unless path.kind_of? Pathname
231
+
232
+ mime = MIME::Types.type_for(path.to_s)[0] || MIME::Types["application/octet-stream"][0]
233
+ uri = endPoint("/#{id}")
234
+ request = Net::HTTP::Post.new(uri)
235
+ ret = nil
236
+
237
+ path.open do |io|
238
+ request.body_stream = io
239
+ set_def_headers(request)
240
+ request.content_length = path.size
241
+ request.content_type = mime.simplified
242
+ ret = workit(request)
243
+ end
244
+ ret
245
+ end
246
+
247
+ ## Delete data for content item
248
+ # Note that the content item is still present and listed.
249
+ def remove_content(id)
250
+ delete("/#{id}")
251
+ end
252
+
253
+ end
254
+
255
+ ##
256
+ # This is not applicable to Murano. Remove?
257
+ # :nocov:
258
+ class ProductModel < ProductBase
259
+ def initialize
260
+ super
261
+ @uriparts << :proxy
262
+ @uriparts << :provision
263
+ @uriparts << :manage
264
+ @uriparts << :model
265
+ end
266
+
267
+ # In Murano, there should only ever be one.
268
+ # AND it should be @pid
269
+ def list
270
+ get('/')
271
+ end
272
+
273
+ def info(modelID=@pid)
274
+ get("/#{modelID}")
275
+ end
276
+
277
+ def list_sn(modelID=@pid)
278
+ get("/#{modelID}/")
279
+ end
280
+ end
281
+
282
+ class ProductSerialNumber < ProductBase
283
+ def initialize
284
+ super
285
+ @uriparts << :proxy
286
+ @uriparts << :provision
287
+ @uriparts << :manage
288
+ @uriparts << :model
289
+ @uriparts << @pid
290
+ end
291
+
292
+ def list(offset=0, limit=1000)
293
+ ret = get("/?offset=#{offset}&limit=#{limit}&status=true")
294
+ return [] if ret.kind_of?(Hash)
295
+ CSV.parse(ret)
296
+ end
297
+
298
+ def logs(sn)
299
+ get("/#{sn}?show=log") # results are empty
300
+ end
301
+
302
+ def regen(sn)
303
+ postf("/#{sn}", {:enable=>true})
304
+ end
305
+
306
+ def disable(sn)
307
+ postf("/#{sn}", {:disable=>true})
308
+ end
309
+
310
+ def activate(sn)
311
+ uri = URI("https://#{@pid}.m2.exosite.com/provision/activate")
312
+ http = Net::HTTP.new(uri.host, uri.port)
313
+ http.use_ssl = true
314
+ http.start
315
+ request = Net::HTTP::Post.new(uri)
316
+ request.form_data = {
317
+ :vendor => @pid,
318
+ :model => @pid,
319
+ :sn => sn
320
+ }
321
+ request['User-Agent'] = "MrMurano/#{MrMurano::VERSION}"
322
+ request['Authorization'] = nil
323
+ request.content_type = 'application/x-www-form-urlencoded; charset=utf-8'
324
+ curldebug(request)
325
+ response = http.request(request)
326
+ case response
327
+ when Net::HTTPSuccess
328
+ return response.body
329
+ else
330
+ showHttpError(request, response)
331
+ end
332
+ end
333
+
334
+ def add_sn(sn, extra='')
335
+ # this does add, but what does that mean?
336
+ # Still need to call …/device/<sn> to enable.
337
+ # How long is the activation window?
338
+ postf('/', {:sn=>sn,:extra=>extra})
339
+ end
340
+
341
+ def remove_sn(sn)
342
+ #postf('/', {:sn=>sn, :delete=>true})
343
+ delete("/#{sn}")
344
+ end
345
+
346
+ def ranges
347
+ get('/?show=ranges')
348
+ end
349
+
350
+ def add_range()
351
+ post('/', {:ranges=>[ ]})
352
+ end
353
+
354
+ end
355
+ # :nocov:
356
+
357
+ end
358
+ # vim: set ai et sw=2 ts=2 :