2 * This module provides the data model layer for the Launchpad Mocklet
5 var util
= require('util');
6 var uuid
= require('node-uuid');
7 var _
= require('lodash');
10 var simmp_module
= require('./simmp.js');
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');
20 * Generic to throw on data model exceptions
22 function DataModelException(message
) {
23 this.message
= message
;
24 this.name
= "DataModelException";
29 * This function is temporary until all needed features are implemented in this mocklet
31 function NotImplementedException(message
) {
32 this.message
= "You have fallen off the edge of the world: "+message
;
33 this.name
= 'NotImplementedException';
38 * Class to handle simulating events over time for monitoring params
40 MonitoringParam = function(values
, time_function
) {
42 this.timeFunc
= time_function
;
45 MonitoringParam
.prototype.timeStep = function(elapsed_seconds
) {
46 this.values
.current_value
= this.timeFunc(this.values
.current_value
,
48 return this.values
.current_value
;
52 * DataModel constructor
55 * restconf_host - Host name and port. eg: 'localhost:8008'
57 DataModel = function (restconf_host
) {
58 this.restconf_host
= restconf_host
? restconf_host
: "localhost:8008";
60 this.simmp
= new simmp_module
.SimMp(simmp_rules
);
62 throw "simmp failed to initialize";
64 // Time data for event simulation (monitoring params)
65 this.start_time
= Date
.now();
66 this.previous_time
=this.start_time
;
69 this.descriptors
= { nsd
: {}, vnfd
: {}, vld
: {} };
71 // Store instance config data. Currently only NS Yang implements config data
72 this.config_records
= { nsr
: {}, vnfr
: {}, vlr
: {} };
74 // Stores Virtual Network Function instance records
75 this.vnfr_records
= { };
77 // Stores Network Service instance operational records
78 this.ns_opdata_records
= { };
80 // Manage which mock data to use next
81 this.vnfr_template_index
= 0;
82 this.nsr_template_index
= 0;
84 // Operational (running) state for opdata records
87 this.opstate
= { nsr
: {}, vnfr
: {} };
89 // Store MonitoringParam objects
90 this.monitoring_params
= {nsr
: {}, vnfr
: {} };
95 * creates a descriptor name from the record name
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';
103 throw new DataModelException('"%s" is not a supported record type', record_type
);
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
);
112 this.descriptors
[descriptor_type
][descriptor
.id
] = descriptor
;
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
);
120 this.config_records
[record_type
][record
.id
] = record
;
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
];
134 DataModel
.prototype.updateControlParam = function(record_type
, record_id
,
136 if (record_type
== 'vnfr') {
137 var record
= this.vnfr_records
[record_id
];
139 var record
= this.ns_opdata_records
[record_id
];
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
) {
149 record
.control_param
[i
].current_value
= value
;
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
);
160 var errmsg
= 'Record type "' + record_type
+ '" with id "'+
161 record_id
+ '" does not have any control params';
162 throw new DataModelException(errmsg
);
169 * General comments on NS instance config/opdata:
170 * For each ns-instance-config, the descriptor needs to be added first
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;
183 DataModel
.prototype.getNsdConnectionPoints = function(nsd_id
) {
184 var nsd
= this.descriptors
['nsd'][nsd_id
];
186 throw new DataModelException("NSD ID '%s' does not exist", nsd_id
);
188 // console.log("\n\nnsd = %s", JSON.stringify(nsd));
189 return nsd
['connection_point'];
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.
199 var control_params
= [];
201 return control_params
;
205 * Sets an ns-instance-config object record and creates an
206 * ns-instance-opdata record.
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.
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
217 // TODO: Consider creating a 'set_or_update' method for ns-instance-config
218 var ns_config
= this.findConfigRecord('nsr', ns_instance_config
.id
);
220 ns_config
.admin_status
= ns_instance_config
.admin_status
;
222 this.setConfigRecord('nsr', ns_instance_config
);
223 ns_config
= ns_instance_config
;
225 if (ns_config
.id
in this.ns_opdata_records
) {
226 delete this.ns_opdata_records
[ns_config
.id
];
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
;
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');
241 var descriptor
= this.descriptors
[descriptor_type
][descriptor_id
];
242 var a_simmp
= this.simmp
;
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
);
250 console
.log('Descriptor(type=%s) with (id=%s) does not have ' +
251 'monitoring params', descriptor_type
, descriptor_id
);
255 throw new DataModelException("Cannot find descriptor %s with id '%s'",
256 descriptor_type
, descriptor_id
);
260 DataModel
.prototype.updateMonitoringParams = function(instance_type
, instance_id
) {
261 var sim_mp
= this.monitoring_params
[instance_type
][instance_id
];
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
);
269 this.previous_time
= time_now
;
270 return monitoring_params
;
272 // TODO: Figure out hosw we want to handle this case
278 * Creates an ns-instance-opdata object, but does not add it to the data
281 DataModel
.prototype.generateNsInstanceOpdata = function (ns_config
) {
282 var nsr_template
= this.nextNsrTemplate();
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
287 var vnfr_template
= this.nextVnfrTemplate();
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
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']
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
);
321 ns_op_rec
= this.ns_opdata_records
[config_record_id
];
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
);
328 console
.log('NO RECORD FOUND for ns config id: %s', config_record_id
);
331 console
.log('Either no admin status record or not enabled');
334 return opdata_records
;
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.
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;
354 return vnfr_template
;
359 * vnfd - VNF Descriptor object
360 * vnfr_id - VNFR unique identifier
361 * host - host name and port
363 DataModel
.prototype.createVnfrActionParams = function(vnfd
, vnfr_id
) {
364 // Canned start, stop for now
365 // TBD: read action params from VNFD and create here
371 description
: "Start this VNFR",
372 group_tag
: "start-vnfr",
373 url
: "https://"+this.restconf_host
+"/api/operations/start-vnfr",
375 payload
: '{"start-vnfr": { "id": "'+vnfr_id
+'"}}'
380 description
: "Stop this VNFR",
381 group_tag
: "stop-vnfr",
382 url
: "https://"+this.restconf_host
+"/api/operations/stop-vnfr",
384 payload
: '{"stop-vnfr": { "id": "'+vnfr_id
+'"}}'
390 DataModel
.prototype.createVnfrControlParams = function(vnfd
, vnfr_id
,
392 console
.log("Called Datamodel.prototype.createVnfrControlParams");
394 console
.log("returning clone of vnfr_template['control_param']");
395 return _
.clone(vnfr_template
['control_param'], true);
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");
409 return control_params
;
413 throw new NotImplementedException("createVnfrControlParam: non-template");
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
422 DataModel
.prototype.createVnfr = function(vnfd
) {
423 //var vnfr_template = this.nextVnfrTemplate();
424 var vnfr_id
= uuid
.v1();
428 // Hack: Copy the VNFD values but append '-Record' to end
429 name
: vnfd
.name
+ ' Record',
430 short_name
: vnfd
.short_name
+ '_REC',
432 description
: vnfd
.description
,
433 version
: vnfd
.version
,
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
)
447 * Creates and adds a new VNFD and matching VNFR record to our data store
449 * TODO: Might need to be updated so we create a VNFR when a start VNFR is called
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
);
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
467 this.vnfr_records
[vnfr
.id
] = vnfr
;
474 DataModel
.prototype.getVnfrs = function () {
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(
481 records
.push(vnfr_record
);
487 // Move the following to a new VnfrManager class
489 DataModel
.prototype.startVnfr = function(vnfr_id
) {
490 console
.log('Calling DataModel.startVnfr with id "%s"', vnfr_id
);
492 console
.log('Here are the VNFR ids we have:');
493 for (key
in this.vnfr_records
) {
494 console
.log('id:%s"', key
);
496 //console.log('vnfr_records = %s', JSON.stringify(this.vnfr_records));
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
);
504 this.opstate
.vnfr
[vnfr_id
] = 'ON';
505 return this.vnfr_records
[vnfr_id
];
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
);
516 this.opstate
.vnfr
[vnfr_id
] = 'OFF';
517 return this.vnfr_records
[vnfr_id
];
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
);
525 if (vnfr_id
in this.opstate
.vnfr
) {
526 return this.opstate
.vnfr
[vnfr_data
];
528 // Assume we are 'ON'
534 /* ==========================
535 * Debug and helper functions
536 * ==========================
539 DataModel
.prototype.prettyPrint = function (out
) {
540 if (out
== undefined) {
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
]));
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
]));
564 DataModelException
: DataModelException
,
565 NotImplementedException
: NotImplementedException
,
566 MonitoringParam
: MonitoringParam
,