github.com/jbking/gohan@v0.0.0-20151217002006-b41ccf1c2a96/etc/extensions/donburi.js (about) 1 // Copyright (C) 2015 NTT Innovation Institute, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 // implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 var donburi = { 17 plugin: {}, 18 no_template_keywords: ['eval', 'block', 'define', 19 'resources', 20 'netconf_close', 'netconf_exec', 21 'ssh_close', 'ssh_exec'], 22 register_plugin: function(name, plugin){ 23 this.plugin[name] = plugin 24 }, 25 apply_template: function(context, data){ 26 var self = this; 27 if(_.isString(data)){ 28 return gohan_template(data, context); 29 } 30 if(_.isArray(data)){ 31 var result = []; 32 _.each(data, function(sub_data){ 33 result.push(self.apply_template(context, sub_data)); 34 }); 35 return result; 36 } 37 if(_.isObject(data)){ 38 var result = {}; 39 _.each(data, function(sub_data, key){ 40 result[key] = self.apply_template(context, sub_data); 41 }); 42 return result; 43 } 44 return data 45 } 46 run_task: function(event_type, context, task){ 47 var self = this; 48 if(!_.isUndefined(task.when)){ 49 with(context){ 50 try{ 51 if(eval(task.when) == false){ 52 if(!_.isUndefined(task["else"])){ 53 try{ 54 self.run_tasks(event_type, context, task["else"]) 55 }catch(err){ 56 context.error = err; 57 } 58 } 59 return; 60 } 61 }catch(err){ 62 context.error = err; 63 return; 64 } 65 } 66 } 67 var result; 68 var retry_count = task.retry; 69 if(_.isUndefined(retry_count)){ 70 retry_count = 1; 71 } 72 for(var retry = 0; retry < retry_count; retry++){ 73 try{ 74 _.each(_.keys(task), function(key){ 75 var plugin = self.plugin[key]; 76 if(_.isUndefined(plugin)){ 77 return; 78 } 79 var with_items = task.with_items; 80 var with_dict = task.with_dict; 81 if(_.isUndefined(with_items) && _.isUndefined(with_dict)){ 82 result = self.run_plugin(plugin, event_type, context, key, task[key]); 83 }else{ 84 if(_.isString(with_items)){ 85 with(context){ 86 with_items = eval(with_items); 87 } 88 } 89 if(_.isArray(with_items)){ 90 result = []; 91 _.each(with_items, function(item){ 92 context.item = item; 93 var sub_result = self.run_plugin(plugin, event_type, context, key, task[key]); 94 result.push(sub_result); 95 }); 96 } 97 if(_.isString(with_dict)){ 98 with(context){ 99 with_dict = eval(with_dict); 100 } 101 } 102 if(_.isObject(with_dict)){ 103 result = {}; 104 _.each(with_dict, function(value, item_key){ 105 context.item = {key: item_key, value:value}; 106 var sub_result = self.run_plugin(plugin, event_type, context, key, task[key]); 107 result[key] = sub_result; 108 }); 109 } 110 } 111 }); 112 if(!_.isUndefined(task.register)){ 113 context[task.register] = result; 114 } 115 }catch(err){ 116 context.error = err; 117 if(!_.isUndefined(task.rescue)){ 118 try{ 119 self.run_tasks(event_type, context, task.rescue) 120 }catch(err){ 121 context.error = err; 122 } 123 }else{ 124 throw err; 125 } 126 } 127 if(!_.isUndefined(task.always)){ 128 try{ 129 self.run_tasks(event_type, context, task.always) 130 }catch(err){ 131 context.error = err; 132 } 133 } 134 } 135 return result; 136 }, 137 run_plugin: function(plugin, event_type, context, key, value){ 138 var self = this; 139 if(self.no_template_keywords.indexOf(key) == -1){ 140 value = self.apply_template(context, value); 141 } 142 return plugin(event_type, context, value); 143 } 144 run_tasks: function(event_type, context, tasks){ 145 var self = this; 146 var result; 147 _.each(tasks, function(task){ 148 result = self.run_task(event_type, context, task); 149 }); 150 return result; 151 }, 152 run: function(code){ 153 var self = this; 154 var events = ["post_create", 155 "post_update", 156 "pre_delete", 157 "notification"] 158 _.each(events, function(event_type){ 159 gohan_register_handler(event_type, function(context){ 160 try{ 161 self.run_tasks(event_type, context, code.tasks); 162 }catch(err){ 163 context.error = err; 164 } 165 }); 166 var event_type_in_transaction = event_type + "_in_transaction" 167 gohan_register_handler(event_type_in_transaction, function(context){ 168 try{ 169 self.run_tasks(event_type_in_transaction, context, code.db_tasks); 170 }catch(err){ 171 context.error = err; 172 } 173 }); 174 }); 175 } 176 } 177 178 donburi.register_plugin("define", function(event_type, context, value){ 179 var func_name = value.name; 180 var tasks = value.tasks; 181 donburi.register_plugin(func_name, function(event_type, context, value){ 182 var new_context = _.extend(context, value) 183 return donburi.run_tasks(event_type, new_context, tasks); 184 }); 185 }); 186 187 donburi.register_plugin("debug", function(event_type, context, value){ 188 console.log(value); 189 }); 190 191 donburi.register_plugin("sleep", function(event_type, context, value){ 192 gohan_sleep(value); 193 }); 194 195 donburi.register_plugin("block", function(event_type, context, value){ 196 donburi.run_tasks(event_type, context, value); 197 }); 198 199 donburi.register_plugin("netconf_open", function(event_type, context, value){ 200 return gohan_netconf_open(value.host, value.username) 201 }); 202 203 donburi.register_plugin("netconf_exec", function(event_type, context, value){ 204 var command = donburi.apply_template(context, value.command); 205 with(context){ 206 return gohan_netconf_exec(eval(value.connection), command) 207 } 208 }); 209 210 donburi.register_plugin("netconf_close", function(event_type, context, value){ 211 with(context){ 212 gohan_netconf_close(eval(value)) 213 } 214 }); 215 216 donburi.register_plugin("ssh_open", function(event_type, context, value){ 217 return gohan_ssh_open(value.host, value.username) 218 }); 219 220 donburi.register_plugin("ssh_exec", function(event_type, context, value){ 221 var command = donburi.apply_template(context, value.command); 222 with(context){ 223 return gohan_ssh_exec(eval(value.connection), command) 224 } 225 }); 226 227 donburi.register_plugin("ssh_close", function(event_type, context, value){ 228 with(context){ 229 gohan_ssh_close(eval(value)) 230 } 231 }); 232 233 donburi.register_plugin("resources", function(event_type, context, value){ 234 if(event_type == "pre_delete" || event_type == "pre_delete_in_transaction"){ 235 var reversed = value.slice() 236 reversed.reverse(); 237 donburi.run_tasks(event_type, context, reversed); 238 }else{ 239 donburi.run_tasks(event_type, context, value); 240 } 241 }); 242 243 donburi.register_plugin("command", function(event_type, context, value){ 244 return gohan_exec(value.name, value.args); 245 }); 246 247 donburi.register_plugin("eval", function(event_type, context, value){ 248 with(context){ 249 return eval(value); 250 } 251 }); 252 253 donburi.register_plugin("vars", function(event_type, context, value){ 254 _.each(value, function(value, key){ 255 context[key] = value; 256 }); 257 }); 258 259 donburi.register_plugin("return", function(event_type, context, value){ 260 return value; 261 }); 262 263 donburi.register_plugin("list", function(event_type, context, value){ 264 var transaction = context.transaction; 265 var filter = value.filter || {}; 266 filter.tenant_id = value.tenant_id; 267 return gohan_db_list(transaction, value.schema, filter); 268 }); 269 270 donburi.register_plugin("fetch", function(event_type, context, value){ 271 var transaction = context.transaction; 272 var value = gohan_db_fetch(transaction, value.schema, value.id, value.tenant_id); 273 return value 274 }); 275 276 donburi.register_plugin("resource", function(event_type, context, value){ 277 var transaction = context.transaction; 278 if(event_type == "post_create_in_transaction" || event_type == "post_create"){ 279 if(_.isUndefined(value.id)){ 280 value.id = gohan_uuid(); 281 } 282 value.properties.id = value.id; 283 var result = gohan_db_create(transaction, value.schema, value.properties); 284 return result; 285 } 286 var id = value.id; 287 if(event_type == "post_update_in_transaction" || event_type == "post_update"){ 288 return gohan_db_update(transaction, value.schema, value.properties); 289 } 290 if(event_type == "pre_delete_in_transaction" || event_type == "pre_delete"){ 291 return gohan_db_delete(transaction, value.schema, id); 292 } 293 return 294 }); 295 296 donburi.register_plugin("update", function(event_type, context, value){ 297 var transaction = context.transaction; 298 var resource = gohan_db_fetch(transaction, value.schema, value.properties.id, ""); 299 if(_.isUndefined(resource)){ 300 return 301 } 302 _.each(value.properties, function(value, key){ 303 resource[key] = value; 304 }); 305 return gohan_db_update(transaction, value.schema, resource); 306 }); 307 308 donburi.register_plugin("delete", function(event_type, context, value){ 309 var transaction = context.transaction; 310 return gohan_db_delete(transaction, value.schema, value.id); 311 }); 312 313 donburi.register_plugin("contrail", function(event_type, context, value){ 314 var schema_id = value.schema; 315 if(_.isUndefined(contrail_donburi[schema_id])){ 316 return contrail_donburi["generic"](event_type, context, value); 317 } 318 return contrail_donburi[schema_id](event_type, context, value); 319 }); 320 321 donburi.register_plugin("contrail_fetch", function(event_type, context, value){ 322 var schema_id = value.schema; 323 var id = value.id; 324 var token = context.auth_token; 325 return contrail_donburi.get_request(token, schema_id, id); 326 }); 327 328 donburi.register_plugin("contrail_list", function(event_type, context, value){ 329 var schema_id = value.schema; 330 var token = context.auth_token; 331 return contrail_donburi.get_request(token, schema_id); 332 }); 333 334 donburi.register_plugin("heat", function(event_type, context, value){ 335 return heat_donburi["stack"](event_type, context, value); 336 }); 337 338 var contrail_donburi = { 339 api_url: function(schema_id, id){ 340 if(_.isUndefined(id)){ 341 return CONTRAIL_URL + schema_id + "s"; 342 } 343 return CONTRAIL_URL + schema_id + "/" + id; 344 }, 345 create_request: function(schema_id, token, data){ 346 var self = this; 347 var request = {}; 348 request[schema_id] = data; 349 var response = gohan_http("POST", self.api_url(schema_id), 350 {"X-Auth-Token": token, 351 "Content-Type": "application/json" 352 }, 353 request) 354 var result = {} 355 result.status_code = parseInt(response.status_code); 356 result.body = response.body; 357 if(result.status_code == 200){ 358 result["data"] = JSON.parse(response.body); 359 } 360 return result; 361 }, 362 update_request: function(schema_id, token, id, data){ 363 var self = this; 364 var request = {}; 365 request[schema_id] = data; 366 var response = gohan_http("PUT", self.api_url(schema_id, id), 367 {"X-Auth-Token": token, 368 "Content-Type": "application/json" 369 }, 370 request) 371 var result = {} 372 result.status_code = parseInt(response.status_code); 373 result.body = response.body; 374 if(result.status_code == 200){ 375 result["data"] = JSON.parse(response.body); 376 } 377 return result; 378 }, 379 delete_request: function(schema_id, token, id){ 380 var self = this; 381 var response = gohan_http( 382 "DELETE", 383 self.api_url(schema_id, id), 384 {"X-Auth-Token": token}, null); 385 var result = {} 386 result.status_code = parseInt(response.status_code); 387 result.body = response.body; 388 return result; 389 }, 390 get_request: function(token, schema_id, id){ 391 var self = this; 392 var response = gohan_http( 393 "GET", 394 self.api_url(schema_id, id), 395 {"X-Auth-Token": token}, null); 396 var result = {} 397 result.status_code = response.status_code; 398 if(response.status_code == "200"){ 399 result["data"] = JSON.parse(response.body); 400 } 401 return result; 402 }, 403 generic: function(event_type, context, value){ 404 var self = this; 405 var transaction = context.transaction; 406 var schema_id = value.schema; 407 var token = context.auth_token; 408 var data = value.properties; 409 410 if(event_type == "post_create"){ 411 return self.create_request(schema_id, token, data); 412 } 413 414 var id = value.id; 415 if(_.isUndefined(id) || id === ""){ 416 return; 417 } 418 if(event_type == "post_update"){ 419 var update_data = {}; 420 _.each(value.allow_update, function(key){ 421 update_data[key] = data[key]; 422 }); 423 if(update_data == {}){ 424 return; 425 } 426 return self.update_request(schema_id, token, id, update_data); 427 }else if(event_type == "pre_delete"){ 428 return self.delete_request(schema_id, token, id); 429 } 430 431 return; 432 }, 433 "virtual-network-subnet": function (event_type, context, value){ 434 var self = this; 435 var token = context.auth_token; 436 var network_id = value.network_id; 437 var data = value.properties; 438 var network_response = self.get_request(token, "virtual-network", network_id); 439 if( _.isUndefined(network_response.data)){ 440 return 441 } 442 var subnet_strings = data.subnet_cidr.split("/"); 443 data.subnet = { 444 ip_prefix: subnet_strings[0], 445 ip_prefix_len: parseInt(subnet_strings[1]) 446 } 447 var network = network_response.data["virtual-network"]; 448 var network_ipam_refs = network["network_ipam_refs"]; 449 if(_.isUndefined(network_ipam_refs)){ 450 network_ipam_refs = [ 451 { 452 "attr": {"ipam_subnets": []}, 453 "to": ["default-domain", "default-project", "default-network-ipam"] 454 }]; 455 network["network_ipam_refs"] = network_ipam_refs; 456 } 457 var ipam_subnets = network_ipam_refs[0]["attr"]["ipam_subnets"]; 458 if(event_type == "post_create"){ 459 ipam_subnets.push(data); 460 } 461 if(event_type == "post_update"){ 462 _.each(ipam_subnets, function(subnet){ 463 if( subnet.subnet_uuid != data.subnet_uuid ){ 464 return 465 } 466 _.each(data, function(key){ 467 subnet[key] = data[key]; 468 }); 469 }); 470 } 471 if(event_type == "pre_delete"){ 472 var new_ipam_subnets = [] 473 _.each(ipam_subnets, function(subnet){ 474 if( subnet.subnet_uuid != data.subnet_uuid ){ 475 new_ipam_subnets.push(subnet); 476 } 477 }); 478 network_ipam_refs[0]["attr"]["ipam_subnets"] = new_ipam_subnets; 479 } 480 var result = self.update_request("virtual-network", token, network_id, network); 481 return result; 482 } 483 }; 484 485 //TODO(nati) This is experimental. 486 //We need to use output on keystone context 487 488 var heat_donburi = { 489 api_url: function(url, schema_id, id){ 490 var base_url = url + "/" + schema_id + "s"; 491 if(_.isUndefined(id)){ 492 return base_url; 493 } 494 return base_url + "/" + id; 495 }, 496 create_request: function(url, schema_id, token, data){ 497 var self = this; 498 var response = gohan_http("POST", self.api_url(url, schema_id), 499 {"X-Auth-Token": token, 500 "Content-Type": "application/json" 501 }, 502 data) 503 var result = {} 504 result.status_code = parseInt(response.status_code); 505 result.body = response.body; 506 if(result.status_code == 201){ 507 result["data"] = JSON.parse(response.body); 508 } 509 return result; 510 }, 511 update_request: function(url, schema_id, token, id, data){ 512 var self = this; 513 var request = {}; 514 request[schema_id] = data; 515 var response = gohan_http("PUT", self.api_url(url, schema_id, id), 516 {"X-Auth-Token": token, 517 "Content-Type": "application/json" 518 }, 519 request) 520 var result = {} 521 result.status_code = parseInt(response.status_code); 522 if(result.status_code == 200){ 523 result["data"] = JSON.parse(response.body); 524 } 525 return result; 526 }, 527 delete_request: function(url, schema_id, token, id){ 528 var self = this; 529 var response = gohan_http( 530 "DELETE", 531 self.api_url(url, schema_id, id), 532 {"X-Auth-Token": token}, null); 533 var result = {} 534 result.status_code = parseInt(response.status_code); 535 return result; 536 }, 537 get_request: function(url, schema_id, token, id){ 538 var self = this; 539 var response = gohan_http( 540 "GET", 541 self.api_url(schema_id, id), 542 {"X-Auth-Token": token}, null); 543 var result = {} 544 result.status_code = parseInt(response.status_code); 545 if(response.status_code == 200){ 546 result["data"] = JSON.parse(response.body); 547 } 548 return result; 549 }, 550 get_endpoint: function(context, endpoint_interface, type){ 551 var url = ""; 552 _.each(context.catalog, function(catalog){ 553 if(catalog.Type === type){ 554 _.each(catalog.Endpoints, function(endpoint){ 555 if(endpoint.Interface === endpoint_interface){ 556 url = endpoint.URL.replace("%(tenant_id)s", context.tenant); 557 } 558 }); 559 } 560 }) 561 return url; 562 }, 563 stack: function(event_type, context, value){ 564 var self = this; 565 var transaction = context.transaction; 566 var schema_id = "stack"; 567 var tenant_id = context.tenant; 568 var token = context.auth_token; 569 var stack_name = value.stack_name; 570 var url = self.get_endpoint(context, "public", "orchestration"); 571 var data = { 572 stack_name: stack_name, 573 template: value.template, 574 }; 575 576 if(event_type == "post_create"){ 577 var response = self.create_request(url, schema_id, token, data); 578 return response; 579 } 580 581 var id = stack_name + "/" + value.id; 582 if(_.isUndefined(id) || id === ""){ 583 return; 584 } 585 if(event_type == "post_update"){ 586 var update_data = {}; 587 _.each(value.allow_update, function(key){ 588 update_data[key] = data[key]; 589 }); 590 if(update_data == {}){ 591 return; 592 } 593 return self.update_request(url, schema_id, token, id, update_data); 594 }else if(event_type == "pre_delete"){ 595 return self.delete_request(url, schema_id, token, id); 596 } 597 598 return; 599 }, 600 };