MuranoCLI 2.0.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.
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 :