Bug 173 - Creation of additional networks in running NS does not publish VLRs
[osm/SO.git] / rwlaunchpad / mock / data_model.js
1 /*
2 * This module provides the data model layer for the Launchpad Mocklet
3 */
4
5 var util = require('util');
6 var uuid = require('node-uuid');
7 var _ = require('lodash');
8
9 // Our modules
10 var simmp_module = require('./simmp.js');
11
12 // Data packages
13 // TODO: Make these parameters to pass to the data model
14 // instead of hardcoding them as requires here
15 var simmp_rules = require('./data/simmp-rules.json');
16 var nsr_templates = require('./data/nsr-templates.json');
17 var vnfr_templates = require('./data/vnfr-templates.json');
18
19 /*
20 * Generic to throw on data model exceptions
21 */
22 function DataModelException(message) {
23 this.message = message;
24 this.name = "DataModelException";
25 }
26
27 /*
28 * This
29 * This function is temporary until all needed features are implemented in this mocklet
30 */
31 function NotImplementedException(message) {
32 this.message = "You have fallen off the edge of the world: "+message;
33 this.name = 'NotImplementedException';
34 }
35
36
37 /*
38 * Class to handle simulating events over time for monitoring params
39 */
40 MonitoringParam = function(values, time_function) {
41 this.values = values;
42 this.timeFunc = time_function;
43 }
44
45 MonitoringParam.prototype.timeStep = function(elapsed_seconds) {
46 this.values.current_value = this.timeFunc(this.values.current_value,
47 elapsed_seconds);
48 return this.values.current_value;
49 };
50
51 /*
52 * DataModel constructor
53 *
54 * Arguments
55 * restconf_host - Host name and port. eg: 'localhost:8008'
56 */
57 DataModel = function (restconf_host) {
58 this.restconf_host = restconf_host ? restconf_host : "localhost:8008";
59
60 this.simmp = new simmp_module.SimMp(simmp_rules);
61 if (!this.simmp) {
62 throw "simmp failed to initialize";
63 }
64 // Time data for event simulation (monitoring params)
65 this.start_time = Date.now();
66 this.previous_time =this.start_time;
67
68 // Store descriptors
69 this.descriptors = { nsd: {}, vnfd: {}, vld: {} };
70
71 // Store instance config data. Currently only NS Yang implements config data
72 this.config_records = { nsr: {}, vnfr: {}, vlr: {} };
73
74 // Stores Virtual Network Function instance records
75 this.vnfr_records = { };
76
77 // Stores Network Service instance operational records
78 this.ns_opdata_records = { };
79
80 // Manage which mock data to use next
81 this.vnfr_template_index = 0;
82 this.nsr_template_index = 0;
83
84 // Operational (running) state for opdata records
85 // 'on', 'off'
86 // TBD: do restarting
87 this.opstate = { nsr: {}, vnfr: {} };
88
89 // Store MonitoringParam objects
90 this.monitoring_params = {nsr: {}, vnfr: {} };
91 }
92
93
94 /*
95 * creates a descriptor name from the record name
96 */
97 DataModel.prototype.rec2desc = function (record_type) {
98 if (record_type.charAt(record_type.lenth-1) == 'r') {
99 return record_type.slice(0, -1)+'d';
100 } else if (["ns","vnf","vl"].indexOf(record_type_) != -1) {
101 return record_type + 'd';
102 } else {
103 throw new DataModelException('"%s" is not a supported record type', record_type);
104 }
105 };
106
107 DataModel.prototype.setDescriptor = function(descriptor_type, descriptor) {
108 if (!this.descriptors.hasOwnProperty(descriptor_type)) {
109 throw new DataModelException('"%s" is not a supported descriptor type', descriptor_type);
110 }
111
112 this.descriptors[descriptor_type][descriptor.id] = descriptor;
113 };
114
115 DataModel.prototype.setConfigRecord = function(record_type, record) {
116 if (!this.config_records.hasOwnProperty(record_type)) {
117 throw new DataModelException('"%s" is not a supported record type', record_type);
118 }
119
120 this.config_records[record_type][record.id] = record;
121 };
122
123 DataModel.prototype.findConfigRecord = function(record_type, record_id) {
124 if (this.config_records.hasOwnProperty(record_type)) {
125 return this.config_records[record_type][record_id];
126 } else {
127 return null;
128 }
129 };
130
131 /*
132 *
133 */
134 DataModel.prototype.updateControlParam = function(record_type, record_id,
135 control_id, value) {
136 if (record_type == 'vnfr') {
137 var record = this.vnfr_records[record_id];
138 } else {
139 var record = this.ns_opdata_records[record_id];
140 }
141 // find the control param
142 if ('control_param' in record) {
143 for (var i=0; i < record.control_param.length; i++) {
144 if (control_id == record.control_param[i].id) {
145 // Make sure value is within min and max values
146 if (value >= record.control_param[i].min_value &&
147 value <= record.control_param[i].max_value) {
148
149 record.control_param[i].current_value = value;
150 return 'SUCCESS';
151 } else {
152 var errmsg = 'value "'+value+'" out of range. '+
153 'Needs to be within '+ record_control_param[i].min_value +
154 ' and ' + record_control_param[i].max_value;
155 throw new DataModelException(errmsg);
156 }
157 }
158 }
159 } else {
160 var errmsg = 'Record type "' + record_type + '" with id "'+
161 record_id + '" does not have any control params';
162 throw new DataModelException(errmsg);
163 }
164 };
165
166 /*
167 * NS functions
168 *
169 * General comments on NS instance config/opdata:
170 * For each ns-instance-config, the descriptor needs to be added first
171 */
172
173 // TODO: Consolidate the template handling functions
174 DataModel.prototype.nextNsrTemplate = function() {
175 var nsr_template = _.clone(nsr_templates[this.nsr_template_index], true);
176 this.nsr_template_index += 1;
177 if (this.nsr_template_index >= nsr_templates.length) {
178 this.nsr_template_index = 0;
179 }
180 return nsr_template;
181 };
182
183 DataModel.prototype.getNsdConnectionPoints = function(nsd_id) {
184 var nsd = this.descriptors['nsd'][nsd_id];
185 if (!nsd) {
186 throw new DataModelException("NSD ID '%s' does not exist", nsd_id);
187 }
188 // console.log("\n\nnsd = %s", JSON.stringify(nsd));
189 return nsd['connection_point'];
190 };
191
192
193 DataModel.prototype.createNsrControlParams = function(ns_instance_config_id) {
194 // TODO: find all VNFDs associated with this NS instance
195 // then either call this.createVnfrControlParams if you want to talk
196 // VNFR specific control params or we can generalize 'createVnfrControlParams'
197 // to pass in 'record_id' instead of vnfr_id.
198 //
199 var control_params = [];
200
201 return control_params;
202 };
203
204 /*
205 * Sets an ns-instance-config object record and creates an
206 * ns-instance-opdata record.
207 *
208 * If the NS instance opdata record matching the id of the ns-instance-config
209 * already exists, then remove the ns-instance-opdate record and reconstruct.
210 */
211 DataModel.prototype.setNsInstanceConfig = function(ns_instance_config) {
212 // we are updating an existing ns-instance record set
213 // There is an issue that subsequent 'PUT' actions do not transfer
214 // the whole data to the mocklet. So we need to retrieve the existingt
215 // ns-instance-config to get the nsd-ref
216
217 // TODO: Consider creating a 'set_or_update' method for ns-instance-config
218 var ns_config = this.findConfigRecord('nsr', ns_instance_config.id);
219 if (ns_config) {
220 ns_config.admin_status = ns_instance_config.admin_status;
221 } else {
222 this.setConfigRecord('nsr', ns_instance_config);
223 ns_config = ns_instance_config;
224 }
225 if (ns_config.id in this.ns_opdata_records) {
226 delete this.ns_opdata_records[ns_config.id];
227 }
228 // if ns-instance-config is 'ENABLED', then create an ns-instance-opdata
229 if (ns_config.admin_status == 'ENABLED') {
230 ns_opdata = this.generateNsInstanceOpdata(ns_config);
231 // set the ns instance opdata. Doesn't matter if it already exists
232 this.ns_opdata_records[ns_opdata.ns_instance_config_ref] = ns_opdata;
233 }
234 };
235
236 DataModel.prototype.generateMonitoringParams = function(descriptor_type, descriptor_id) {
237 console.log('Called generateMonitoringParams');
238 if (!(descriptor_type in this.descriptors)) {
239 throw DataModelException('descriptor type "%s" not found');
240 }
241 var descriptor = this.descriptors[descriptor_type][descriptor_id];
242 var a_simmp = this.simmp;
243 if (descriptor) {
244 if ('monitoring_param' in descriptor) {
245 return descriptor['monitoring_param'].map(function(obj) {
246 var simFunc = a_simmp.createSimMonitorFunc(obj);
247 return new MonitoringParam(_.clone(obj, true), simFunc);
248 });
249 } else {
250 console.log('Descriptor(type=%s) with (id=%s) does not have ' +
251 'monitoring params', descriptor_type, descriptor_id);
252 return [];
253 }
254 } else {
255 throw new DataModelException("Cannot find descriptor %s with id '%s'",
256 descriptor_type, descriptor_id);
257 }
258 };
259
260 DataModel.prototype.updateMonitoringParams = function(instance_type, instance_id) {
261 var sim_mp = this.monitoring_params[instance_type][instance_id];
262 if (sim_mp) {
263 var time_now = Date.now();
264 var elapsed_seconds = (time_now - this.previous_time) / 1000;
265 var monitoring_params = sim_mp.map(function(obj) {
266 obj.timeStep(elapsed_seconds);
267 return obj.values;
268 });
269 this.previous_time = time_now;
270 return monitoring_params;
271 } else {
272 // TODO: Figure out hosw we want to handle this case
273 return [];
274 }
275 };
276
277 /*
278 * Creates an ns-instance-opdata object, but does not add it to the data
279 * store.
280 */
281 DataModel.prototype.generateNsInstanceOpdata = function (ns_config) {
282 var nsr_template = this.nextNsrTemplate();
283
284 // HACK: We need to get control and action param from the nsr
285 // or have a function that synchronizes the next array element in
286 // the templates
287 var vnfr_template = this.nextVnfrTemplate();
288
289 var nsd_id = ns_config.nsd_ref;
290 var connection_points = this.getNsdConnectionPoints(ns_config.nsd_ref);
291 var sim_mp = this.generateMonitoringParams('nsd', nsd_id);
292 // save for using in update
293 this.monitoring_params['nsr'][ns_config.id] = sim_mp;
294 var monitoring_params = sim_mp.map(function(obj) {
295 // not time stepping when we create them
296 return obj.values;
297 });
298
299 return {
300 ns_instance_config_ref: ns_config.id,
301 'connection_point' : _.clone(connection_points, true),
302 epa_param: _.clone(nsr_template['epa_param'], true),
303 // NOTE: Remarked out until nfvi metrics figured out
304 //nfvi_metric: _.clone(nsr_template['nfvi_metric'], true),
305 monitoring_param: monitoring_params,
306 //monitoring_param: _.clone(nsr_template['monitoring_param'], true),
307 create_time: nsr_template['create_time'],
308 action_param: vnfr_template['action_param'],
309 // TODO: control_param: this.createNsrControlParams(ns_config.id);
310 control_param: vnfr_template['control_param']
311 };
312 };
313
314 DataModel.prototype.getNsInstanceOpdata = function() {
315 var opdata_records = [];
316 var config_records = this.config_records['nsr'];
317 for (config_record_id in config_records) {
318 if (config_records[config_record_id]['admin_status'] == 'ENABLED') {
319 console.log('Is ENABLED: ns-instance-config record with id %s', config_record_id);
320
321 ns_op_rec = this.ns_opdata_records[config_record_id];
322 if (ns_op_rec) {
323 // TODO: update monitoring params
324 ns_op_rec.monitoring_param = this.updateMonitoringParams(
325 'nsr', config_record_id);
326 opdata_records.push(ns_op_rec);
327 } else {
328 console.log('NO RECORD FOUND for ns config id: %s', config_record_id);
329 }
330 } else {
331 console.log('Either no admin status record or not enabled');
332 }
333 }
334 return opdata_records;
335 };
336
337
338 /* =============
339 * VNF functions
340 * =============
341 */
342
343 /*
344 * Gets the next VNFR template from the array of VNFR templates and
345 * increments the VNFR template counter. Wraps back to the first VNFR
346 * template when the last one is used.
347 */
348 DataModel.prototype.nextVnfrTemplate = function() {
349 var vnfr_template = _.clone(vnfr_templates[this.vnfr_template_index], true);
350 this.vnfr_template_index += 1;
351 if (this.vnfr_template_index >= vnfr_templates.length) {
352 this.vnfr_template_index = 0;
353 }
354 return vnfr_template;
355 }
356
357 /*
358 * Arguments
359 * vnfd - VNF Descriptor object
360 * vnfr_id - VNFR unique identifier
361 * host - host name and port
362 */
363 DataModel.prototype.createVnfrActionParams = function(vnfd, vnfr_id) {
364 // Canned start, stop for now
365 // TBD: read action params from VNFD and create here
366 // Use
367 var action_param = [
368 {
369 id: uuid.v1(),
370 name: "Start Me",
371 description: "Start this VNFR",
372 group_tag: "start-vnfr",
373 url: "https://"+this.restconf_host+"/api/operations/start-vnfr",
374 operation: "POST",
375 payload: '{"start-vnfr": { "id": "'+vnfr_id+'"}}'
376 },
377 {
378 id: uuid.v1(),
379 name: "Stop Me",
380 description: "Stop this VNFR",
381 group_tag: "stop-vnfr",
382 url: "https://"+this.restconf_host+"/api/operations/stop-vnfr",
383 operation: "POST",
384 payload: '{"stop-vnfr": { "id": "'+vnfr_id+'"}}'
385 }
386 ];
387 return action_param;
388 };
389
390 DataModel.prototype.createVnfrControlParams = function(vnfd, vnfr_id,
391 vnfr_template) {
392 console.log("Called Datamodel.prototype.createVnfrControlParams");
393 if (vnfr_template) {
394 console.log("returning clone of vnfr_template['control_param']");
395 return _.clone(vnfr_template['control_param'], true);
396 } else {
397 if (vnfd.control_param) {
398 console.log("VNFD's control-param="+JSON.stringify(vnfd.control_param));
399 var a_restconf_host = this.restconf_host;
400 var cp_arry = _.clone(vnfd.control_param, true);
401 var control_params = vnfd.control_param.map(function(obj) {
402 var cp = _.clone(obj, true);
403 cp.url = util.format(cp.url, a_restconf_host);
404 console.log("\ncontrol-param payload before:"+ cp.payload);
405 cp.payload = util.format(cp.payload, vnfr_id);
406 console.log("\ncontrol-param payload after:"+ cp.payload+"\n");
407 return cp;
408 });
409 return control_params;
410 } else {
411 return [];
412 }
413 throw new NotImplementedException("createVnfrControlParam: non-template");
414 }
415 }
416
417 /*
418 * Creates a new VNFR base on the VNFD in the argument.
419 * This method is intended to not have side effects, otherwise
420 * just put this code in this.addVnfData
421 */
422 DataModel.prototype.createVnfr = function(vnfd) {
423 //var vnfr_template = this.nextVnfrTemplate();
424 var vnfr_id = uuid.v1();
425
426 return {
427 id: vnfr_id,
428 // Hack: Copy the VNFD values but append '-Record' to end
429 name: vnfd.name + ' Record',
430 short_name: vnfd.short_name + '_REC',
431 vendor: vnfd.vendor,
432 description: vnfd.description,
433 version: vnfd.version,
434 vnfd_ref: vnfd.id,
435 internal_vlr: [],
436 // Even though this is in the Yang, it doesn't exist in the
437 // instantiated model:
438 // 'internal_connection_point_ref': [],
439 action_param: this.createVnfrActionParams(vnfd, vnfr_id),
440 //control_param: _.clone(vnfr_template['control_param'], true)
441 control_param: this.createVnfrControlParams(vnfd, vnfr_id)
442 };
443 };
444
445
446 /*
447 * Creates and adds a new VNFD and matching VNFR record to our data store
448 *
449 * TODO: Might need to be updated so we create a VNFR when a start VNFR is called
450 *
451 */
452 DataModel.prototype.addVnfData = function(vnfd) {
453 // if the vnfd does not already exist:
454 if (this.descriptors['vnfd'][vnfd.id] == null) {
455 console.log("adding new vnfd with id %s", vnfd.id);
456 this.setDescriptor('vnfd', vnfd);
457 // create a vnfr record, but without monitoring-param
458 var vnfr = this.createVnfr(vnfd);
459
460 var sim_mp = this.generateMonitoringParams('vnfd', vnfd.id);
461 // save for using in update
462 this.monitoring_params['vnfr'][vnfr.id] = sim_mp;
463 vnfr.monitoring_param = sim_mp.map(function(obj) {
464 // not time stepping when we create them
465 return obj.values;
466 });
467 this.vnfr_records[vnfr.id] = vnfr;
468 } else {
469 // do nothing
470 }
471 };
472
473
474 DataModel.prototype.getVnfrs = function () {
475 records = [];
476 for (vnfr_id in this.vnfr_records) {
477 // When admin-status is implemented, then return only those 'ENABLED'
478 var vnfr_record = this.vnfr_records[vnfr_id];
479 vnfr_record.monitoring_param = this.updateMonitoringParams(
480 'vnfr', vnfr_id);
481 records.push(vnfr_record);
482 }
483 return records;
484 }
485
486
487 // Move the following to a new VnfrManager class
488
489 DataModel.prototype.startVnfr = function(vnfr_id) {
490 console.log('Calling DataModel.startVnfr with id "%s"', vnfr_id);
491
492 console.log('Here are the VNFR ids we have:');
493 for (key in this.vnfr_records) {
494 console.log('id:%s"', key);
495 }
496 //console.log('vnfr_records = %s', JSON.stringify(this.vnfr_records));
497
498 if (!(vnfr_id in this.vnfr_records)) {
499 var errmsg = 'Cannot find vnfr record with id "'+vnfr_id+'"';
500 console.error('\n\n'+errmsg+'\n\n');
501 throw new DataModelException(errmsg);
502 }
503 // Just add/set it
504 this.opstate.vnfr[vnfr_id] = 'ON';
505 return this.vnfr_records[vnfr_id];
506 }
507
508 DataModel.prototype.stopVnfr = function(vnfr_id) {
509 console.log('Calling DataModel.stopVnfr with id "%s"', vnfr_id);
510 if (!(vnfr_id in this.vnfr_records)) {
511 var errmsg = 'Cannot find vnfr record with id "'+vnfr_id+'"';
512 console.error(errmsg);
513 throw new DataModelException(errmsg);
514 }
515 // Just add/set it
516 this.opstate.vnfr[vnfr_id] = 'OFF';
517 return this.vnfr_records[vnfr_id];
518 }
519
520 DataModel.prototype.vnfrRunningState = function(vnfr_id) {
521 if (!(vnfr_id in this.vnfr_records)) {
522 throw new DataModelException(
523 'DataModel.stopVnfr: Cannot find VNFR with id "%s"', vnfr_id);
524 }
525 if (vnfr_id in this.opstate.vnfr) {
526 return this.opstate.vnfr[vnfr_data];
527 } else {
528 // Assume we are 'ON'
529 return 'ON';
530 }
531 }
532
533
534 /* ==========================
535 * Debug and helper functions
536 * ==========================
537 */
538
539 DataModel.prototype.prettyPrint = function (out) {
540 if (out == undefined) {
541 out = console.log;
542 }
543 out('Descriptors:');
544 for (descriptor_type in this.descriptors) {
545 out("Descriptor type: %s", descriptor_type);
546 for (descriptor_id in this.descriptors[descriptor_type]) {
547 out("data=%s",descriptor_id,
548 JSON.stringify(this.descriptors[descriptor_type][descriptor_id]));
549 };
550 };
551
552 out('\nConfigRecords:');
553 for (record_type in this.config_records) {
554 out("Record type: %s", record_type);
555 for (record_id in this.config_records[record_type]) {
556 out("data=%s", record_id,
557 JSON.stringify(this.config_records[record_type][record_id]));
558 };
559 };
560 };
561
562
563 module.exports = {
564 DataModelException: DataModelException,
565 NotImplementedException: NotImplementedException,
566 MonitoringParam: MonitoringParam,
567 DataModel: DataModel
568 };
569