Andrew Yourtchenko | fa1456a | 2016-11-11 16:32:52 +0000 | [diff] [blame] | 1 | --[[ |
| 2 | /* |
| 3 | * Copyright (c) 2016 Cisco and/or its affiliates. |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at: |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | ]] |
| 17 | |
| 18 | -- Experimental prototype CLI using API to VPP, with tab completion |
| 19 | -- |
| 20 | -- Written by Andrew Yourtchenko (ayourtch@cisco.com) 2010,2016 |
| 21 | -- |
| 22 | |
| 23 | vpp = require "vpp-lapi" |
| 24 | |
| 25 | |
| 26 | local dotdotdot = "..." |
| 27 | |
| 28 | -- First the "readline" routine |
| 29 | |
| 30 | readln = { |
| 31 | split = function(str, pat) |
| 32 | local t = {} -- NOTE: use {n = 0} in Lua-5.0 |
| 33 | local fpat = "(.-)" .. pat |
| 34 | local last_end = 1 |
| 35 | if str then |
| 36 | local s, e, cap = str:find(fpat, 1) |
| 37 | while s do |
| 38 | if s ~= 1 or cap ~= "" then |
| 39 | table.insert(t,cap) |
| 40 | end |
| 41 | last_end = e+1 |
| 42 | s, e, cap = str:find(fpat, last_end) |
| 43 | end |
| 44 | if last_end <= #str then |
| 45 | cap = str:sub(last_end) |
| 46 | table.insert(t, cap) |
| 47 | end |
| 48 | end |
| 49 | return t |
| 50 | end, |
| 51 | |
| 52 | reader = function() |
| 53 | local rl = {} |
| 54 | |
| 55 | rl.init = function() |
| 56 | os.execute("stty -icanon min 1 -echo") |
| 57 | rl.rawmode = true |
| 58 | end |
| 59 | |
| 60 | rl.done = function() |
| 61 | os.execute("stty icanon echo") |
| 62 | rl.rawmode = false |
| 63 | end |
| 64 | |
| 65 | rl.prompt = ">" |
| 66 | rl.history = { "" } |
| 67 | rl.history_index = 1 |
| 68 | rl.history_length = 1 |
| 69 | |
| 70 | rl.hide_cmd = function() |
| 71 | local bs = string.char(8) .. " " .. string.char(8) |
| 72 | for i = 1, #rl.command do |
| 73 | io.stdout:write(bs) |
| 74 | end |
| 75 | end |
| 76 | |
| 77 | rl.show_cmd = function() |
| 78 | if rl.command then |
| 79 | io.stdout:write(rl.command) |
| 80 | end |
| 81 | end |
| 82 | |
| 83 | rl.store_history = function(cmd) |
| 84 | if cmd == "" then |
| 85 | return |
| 86 | end |
| 87 | rl.history[rl.history_length] = cmd |
| 88 | rl.history_length = rl.history_length + 1 |
| 89 | rl.history_index = rl.history_length |
| 90 | rl.history[rl.history_length] = "" |
| 91 | end |
| 92 | |
| 93 | rl.readln = function() |
| 94 | local done = false |
| 95 | local need_prompt = true |
| 96 | rl.command = "" |
| 97 | |
| 98 | if not rl.rawmode then |
| 99 | rl.init() |
| 100 | end |
| 101 | |
| 102 | while not done do |
| 103 | if need_prompt then |
| 104 | io.stdout:write(rl.prompt) |
| 105 | io.stdout:write(rl.command) |
| 106 | need_prompt = false |
| 107 | end |
| 108 | |
| 109 | local ch = io.stdin:read(1) |
| 110 | if ch:byte(1) == 27 then |
| 111 | -- CONTROL |
| 112 | local ch2 = io.stdin:read(1) |
| 113 | -- arrows |
| 114 | if ch2:byte(1) == 91 then |
| 115 | local ch3 = io.stdin:read(1) |
| 116 | local b = ch3:byte(1) |
| 117 | if b == 65 then |
| 118 | ch = "UP" |
| 119 | elseif b == 66 then |
| 120 | ch = "DOWN" |
| 121 | elseif b == 67 then |
| 122 | ch = "RIGHT" |
| 123 | elseif b == 68 then |
| 124 | ch = "LEFT" |
| 125 | end |
| 126 | -- print("Byte: " .. ch3:byte(1)) |
| 127 | -- if ch3:byte(1) |
| 128 | end |
| 129 | end |
| 130 | |
| 131 | if ch == "?" then |
| 132 | io.stdout:write(ch) |
| 133 | io.stdout:write("\n") |
| 134 | if rl.help then |
| 135 | rl.help(rl) |
| 136 | end |
| 137 | need_prompt = true |
| 138 | elseif ch == "\t" then |
| 139 | if rl.tab_complete then |
| 140 | rl.tab_complete(rl) |
| 141 | end |
| 142 | io.stdout:write("\n") |
| 143 | need_prompt = true |
| 144 | elseif ch == "\n" then |
| 145 | io.stdout:write(ch) |
| 146 | done = true |
| 147 | elseif ch == "\004" then |
| 148 | io.stdout:write("\n") |
| 149 | rl.command = nil |
| 150 | done = true |
| 151 | elseif ch == string.char(127) then |
| 152 | if rl.command ~= "" then |
| 153 | io.stdout:write(string.char(8) .. " " .. string.char(8)) |
| 154 | rl.command = string.sub(rl.command, 1, -2) |
| 155 | end |
| 156 | elseif #ch > 1 then |
| 157 | -- control char |
| 158 | if ch == "UP" then |
| 159 | rl.hide_cmd() |
| 160 | if rl.history_index == #rl.history then |
| 161 | rl.history[rl.history_index] = rl.command |
| 162 | end |
| 163 | if rl.history_index > 1 then |
| 164 | rl.history_index = rl.history_index - 1 |
| 165 | rl.command = rl.history[rl.history_index] |
| 166 | end |
| 167 | rl.show_cmd() |
| 168 | elseif ch == "DOWN" then |
| 169 | rl.hide_cmd() |
| 170 | if rl.history_index < rl.history_length then |
| 171 | rl.history_index = rl.history_index + 1 |
| 172 | rl.command = rl.history[rl.history_index] |
| 173 | end |
| 174 | rl.show_cmd() |
| 175 | end |
| 176 | else |
| 177 | io.stdout:write(ch) |
| 178 | rl.command = rl.command .. ch |
| 179 | end |
| 180 | end |
| 181 | if rl.command then |
| 182 | rl.store_history(rl.command) |
| 183 | end |
| 184 | return rl.command |
| 185 | end |
| 186 | return rl |
| 187 | end |
| 188 | |
| 189 | } |
| 190 | |
| 191 | --[[ |
| 192 | |
| 193 | r = reader() |
| 194 | |
| 195 | local done = false |
| 196 | |
| 197 | while not done do |
| 198 | local cmd = r.readln() |
| 199 | print("Command: " .. tostring(cmd)) |
| 200 | if not cmd or cmd == "quit" then |
| 201 | done = true |
| 202 | end |
| 203 | end |
| 204 | |
| 205 | r.done() |
| 206 | |
| 207 | ]] |
| 208 | |
| 209 | --------- MDS show tech parser |
| 210 | |
| 211 | local print_section = nil |
| 212 | local list_sections = false |
| 213 | |
| 214 | local curr_section = "---" |
| 215 | local curr_parser = nil |
| 216 | |
| 217 | -- by default operate in batch mode |
| 218 | local batch_mode = true |
| 219 | |
| 220 | local db = {} |
| 221 | local device = {} |
| 222 | device.output = {} |
| 223 | local seen_section = {} |
| 224 | |
| 225 | function start_collection(name) |
| 226 | device = {} |
| 227 | seen_section = {} |
| 228 | end |
| 229 | |
| 230 | function print_error(errmsg) |
| 231 | print("@#$:" .. errmsg) |
| 232 | end |
| 233 | |
| 234 | function keys(tbl) |
| 235 | local t = {} |
| 236 | for k, v in pairs(tbl) do |
| 237 | table.insert(t, k) |
| 238 | end |
| 239 | return t |
| 240 | end |
| 241 | |
| 242 | function tset (parent, ...) |
| 243 | |
| 244 | -- print ('set', ...) |
| 245 | |
| 246 | local len = select ('#', ...) |
| 247 | local key, value = select (len-1, ...) |
| 248 | local cutpoint, cutkey |
| 249 | |
| 250 | for i=1,len-2 do |
| 251 | |
| 252 | local key = select (i, ...) |
| 253 | local child = parent[key] |
| 254 | |
| 255 | if value == nil then |
| 256 | if child == nil then return |
| 257 | elseif next (child, next (child)) then cutpoint = nil cutkey = nil |
| 258 | elseif cutpoint == nil then cutpoint = parent cutkey = key end |
| 259 | |
| 260 | elseif child == nil then child = {} parent[key] = child end |
| 261 | |
| 262 | parent = child |
| 263 | end |
| 264 | |
| 265 | if value == nil and cutpoint then cutpoint[cutkey] = nil |
| 266 | else parent[key] = value return value end |
| 267 | end |
| 268 | |
| 269 | |
| 270 | function tget (parent, ...) |
| 271 | local len = select ('#', ...) |
| 272 | for i=1,len do |
| 273 | parent = parent[select (i, ...)] |
| 274 | if parent == nil then break end |
| 275 | end |
| 276 | return parent |
| 277 | end |
| 278 | |
| 279 | |
| 280 | local pager_lines = 23 |
| 281 | local pager_printed = 0 |
| 282 | local pager_skipping = false |
| 283 | local pager_filter_pipe = nil |
| 284 | |
| 285 | function pager_reset() |
| 286 | pager_printed = 0 |
| 287 | pager_skipping = false |
| 288 | if pager_filter_pipe then |
| 289 | pager_filter_pipe:close() |
| 290 | pager_filter_pipe = nil |
| 291 | end |
| 292 | end |
| 293 | |
| 294 | |
| 295 | function print_more() |
| 296 | io.stdout:write(" --More-- ") |
| 297 | end |
| 298 | |
| 299 | function print_nomore() |
| 300 | local bs = string.char(8) |
| 301 | local bs10 = bs .. bs .. bs .. bs .. bs .. bs .. bs .. bs .. bs .. bs |
| 302 | io.stdout:write(bs10 .. " " .. bs10) |
| 303 | end |
| 304 | |
| 305 | function print_line(txt) |
| 306 | if pager_filter_pipe then |
| 307 | pager_filter_pipe:write(txt .. "\n") |
| 308 | return |
| 309 | end |
| 310 | if pager_printed >= pager_lines then |
| 311 | print_more() |
| 312 | local ch = io.stdin:read(1) |
| 313 | if ch == " " then |
| 314 | pager_printed = 0 |
| 315 | elseif ch == "\n" then |
| 316 | pager_printed = pager_printed - 1 |
| 317 | elseif ch == "q" then |
| 318 | pager_printed = 0 |
| 319 | pager_skipping = true |
| 320 | end |
| 321 | print_nomore() |
| 322 | end |
| 323 | if not pager_skipping then |
| 324 | print(txt) |
| 325 | pager_printed = pager_printed + 1 |
| 326 | else |
| 327 | -- skip printing |
| 328 | end |
| 329 | end |
| 330 | |
| 331 | function paged_write(text) |
| 332 | local t = readln.split(text, "[\n]") |
| 333 | if string.sub(text, -1) == "\n" then |
| 334 | table.insert(t, "") |
| 335 | end |
| 336 | for i, v in ipairs(t) do |
| 337 | if i < #t then |
| 338 | print_line(v) |
| 339 | else |
| 340 | if pager_filter_pipe then |
| 341 | pager_filter_pipe:write(v) |
| 342 | else |
| 343 | io.stdout:write(v) |
| 344 | end |
| 345 | end |
| 346 | end |
| 347 | end |
| 348 | |
| 349 | |
| 350 | |
| 351 | |
| 352 | |
| 353 | function get_choices(tbl, key) |
| 354 | local res = {} |
| 355 | for k, v in pairs(tbl) do |
| 356 | if string.sub(k, 1, #key) == key then |
| 357 | table.insert(res, k) |
| 358 | elseif 0 < #key and dotdotdot == k then |
| 359 | table.insert(res, k) |
| 360 | end |
| 361 | end |
| 362 | return res |
| 363 | end |
| 364 | |
| 365 | function get_exact_choice(choices, val) |
| 366 | local exact_idx = nil |
| 367 | local substr_idx = nil |
| 368 | local substr_seen = false |
| 369 | |
| 370 | if #choices == 1 then |
| 371 | if choices[1] == dotdotdot then |
| 372 | return 1 |
| 373 | elseif string.sub(choices[1], 1, #val) == val then |
| 374 | return 1 |
| 375 | else |
| 376 | return nil |
| 377 | end |
| 378 | else |
| 379 | for i, v in ipairs(choices) do |
| 380 | if v == val then |
| 381 | exact_idx = i |
| 382 | substr_seen = true |
| 383 | elseif choices[i] ~= dotdotdot and string.sub(choices[i], 1, #val) == val then |
| 384 | if substr_seen then |
| 385 | substr_idx = nil |
| 386 | else |
| 387 | substr_idx = i |
| 388 | substr_seen = true |
| 389 | end |
| 390 | elseif choices[i] == dotdotdot then |
| 391 | if substr_seen then |
| 392 | substr_idx = nil |
| 393 | else |
| 394 | substr_idx = i |
| 395 | substr_seen = true |
| 396 | end |
| 397 | end |
| 398 | end |
| 399 | end |
| 400 | return exact_idx or substr_idx |
| 401 | end |
| 402 | |
| 403 | function device_cli_help(rl) |
| 404 | local key = readln.split(rl.command, "[ ]+") |
| 405 | local tree = rl.tree |
| 406 | local keylen = #key |
| 407 | local fullcmd = "" |
| 408 | local error = false |
| 409 | local terse = true |
| 410 | |
| 411 | if ((#rl.command >= 1) and (string.sub(rl.command, -1) == " ")) or (#rl.command == 0) then |
| 412 | table.insert(key, "") |
| 413 | terse = false |
| 414 | end |
| 415 | |
| 416 | for i, v in ipairs(key) do |
| 417 | local choices = get_choices(tree, v) |
| 418 | local idx = get_exact_choice(choices, v) |
| 419 | if idx then |
| 420 | local choice = choices[idx] |
| 421 | tree = tree[choice] |
| 422 | fullcmd = fullcmd .. choice .. " " |
| 423 | else |
| 424 | if i < #key then |
| 425 | error = true |
| 426 | end |
| 427 | end |
| 428 | |
| 429 | if i == #key and not error then |
| 430 | for j, w in ipairs(choices) do |
| 431 | if terse then |
| 432 | paged_write(w .. "\t") |
| 433 | else |
| 434 | paged_write(" " .. w .. "\n") |
| 435 | end |
| 436 | end |
| 437 | paged_write("\n") |
| 438 | if terse then |
| 439 | paged_write(" \n") |
| 440 | end |
| 441 | end |
| 442 | end |
| 443 | pager_reset() |
| 444 | end |
| 445 | |
| 446 | function device_cli_tab_complete(rl) |
| 447 | local key = readln.split(rl.command, "[ ]+") |
| 448 | local tree = rl.tree |
| 449 | local keylen = #key |
| 450 | local fullcmd = "" |
| 451 | local error = false |
| 452 | |
| 453 | for i, v in ipairs(key) do |
| 454 | local choices = get_choices(tree, v) |
| 455 | local idx = get_exact_choice(choices, v) |
| 456 | if idx and choices[idx] ~= dotdotdot then |
| 457 | local choice = choices[idx] |
| 458 | tree = tree[choice] |
| 459 | -- print("level " .. i .. " '" .. choice .. "'") |
| 460 | fullcmd = fullcmd .. choice .. " " |
| 461 | else |
| 462 | -- print("level " .. i .. " : " .. table.concat(choices, " ") .. " ") |
| 463 | error = true |
| 464 | end |
| 465 | end |
| 466 | if not error then |
| 467 | rl.command = fullcmd |
| 468 | else |
| 469 | -- print("\n\nerror\n") |
| 470 | end |
| 471 | pager_reset() |
| 472 | end |
| 473 | |
| 474 | function device_cli_exec(rl) |
| 475 | |
| 476 | local cmd_nopipe = rl.command |
| 477 | local cmd_pipe = nil |
| 478 | |
| 479 | local pipe1, pipe2 = string.find(rl.command, "[|]") |
| 480 | if pipe1 then |
| 481 | cmd_nopipe = string.sub(rl.command, 1, pipe1-1) |
| 482 | cmd_pipe = string.sub(rl.command, pipe2+1, -1) |
| 483 | end |
| 484 | |
| 485 | local key = readln.split(cmd_nopipe .. " <cr>", "[ ]+") |
| 486 | local tree = rl.tree |
| 487 | local keylen = #key |
| 488 | local fullcmd = "" |
| 489 | local error = false |
| 490 | local func = nil |
| 491 | |
| 492 | if cmd_pipe then |
| 493 | pager_filter_pipe = io.popen(cmd_pipe, "w") |
| 494 | end |
| 495 | |
| 496 | |
| 497 | rl.choices = {} |
| 498 | |
| 499 | for i, v in ipairs(key) do |
| 500 | local choices = get_choices(tree, v) |
| 501 | local idx = get_exact_choice(choices, v) |
| 502 | if idx then |
| 503 | local choice = choices[idx] |
| 504 | if i == #key then |
| 505 | func = tree[choice] |
| 506 | else |
| 507 | if choice == dotdotdot then |
| 508 | -- keep the tree the same, update the choice value to match the input string |
| 509 | choices[idx] = v |
| 510 | choice = v |
| 511 | else |
| 512 | tree = tree[choice] |
| 513 | end |
| 514 | end |
| 515 | -- print("level " .. i .. " '" .. choice .. "'") |
| 516 | table.insert(rl.choices, choice) |
| 517 | else |
| 518 | -- print("level " .. i .. " : " .. table.concat(choices, " ") .. " ") |
| 519 | error = true |
| 520 | return nil |
| 521 | end |
| 522 | end |
| 523 | return func |
| 524 | end |
| 525 | |
| 526 | function populate_tree(commands) |
| 527 | local tree = {} |
| 528 | |
| 529 | for k, v in pairs(commands) do |
| 530 | local key = readln.split(k .. " <cr>", "[ ]+") |
| 531 | local xtree = tree |
| 532 | for i, kk in ipairs(key) do |
| 533 | if i == 1 and kk == "sh" then |
| 534 | kk = "show" |
| 535 | end |
| 536 | if i == #key then |
| 537 | if type(v) == "function" then |
| 538 | xtree[kk] = v |
| 539 | else |
| 540 | xtree[kk] = function(rl) paged_write(table.concat(v, "\n") .. "\n") end |
| 541 | end |
| 542 | else |
| 543 | if not xtree[kk] then |
| 544 | xtree[kk] = {} |
| 545 | end |
| 546 | xtree = xtree[kk] |
| 547 | end |
| 548 | end |
| 549 | end |
| 550 | return tree |
| 551 | end |
| 552 | |
| 553 | function trim (s) |
| 554 | return (string.gsub(s, "^%s*(.-)%s*$", "%1")) |
| 555 | end |
| 556 | |
| 557 | |
| 558 | function init_vpp(vpp) |
| 559 | local root_dir = "/home/ubuntu/vpp" |
Andrew Yourtchenko | b868e4e | 2016-12-08 14:03:55 +0000 | [diff] [blame] | 560 | local pneum_path = root_dir .. "/build-root/install-vpp_lite_debug-native/vpp-api/lib64/libpneum.so" |
Andrew Yourtchenko | fa1456a | 2016-11-11 16:32:52 +0000 | [diff] [blame] | 561 | |
| 562 | vpp:init({ pneum_path = pneum_path }) |
| 563 | |
Andrew Yourtchenko | b868e4e | 2016-12-08 14:03:55 +0000 | [diff] [blame] | 564 | vpp:init({ pneum_path = pneum_path }) |
| 565 | vpp:json_api(root_dir .. "/build-root/install-vpp_lite_debug-native/vpp/vpp-api/vpe.api.json") |
| 566 | |
| 567 | |
Andrew Yourtchenko | fa1456a | 2016-11-11 16:32:52 +0000 | [diff] [blame] | 568 | |
| 569 | vpp:connect("lua_cli") |
| 570 | end |
| 571 | |
| 572 | function run_cli(vpp, cli) |
| 573 | local reply = vpp:api_call("cli_inband", { cmd = cli }) |
| 574 | if reply and #reply == 1 then |
| 575 | local rep = reply[1] |
| 576 | if 0 == rep.retval then |
| 577 | return rep.reply |
| 578 | else |
| 579 | return "XXXXXLUACLI: API RETVAL ERROR : " .. tostring(rep.retval) |
| 580 | end |
| 581 | else |
| 582 | return "XXXXXLUACLI ERROR, RAW REPLY: " .. vpp.dump(reply) |
| 583 | end |
| 584 | end |
| 585 | |
| 586 | |
| 587 | function toprintablestring(s) |
| 588 | if type(s) == "string" then |
| 589 | return "\n"..vpp.hex_dump(s) |
| 590 | else |
| 591 | return tostring(s) |
| 592 | end |
| 593 | end |
| 594 | |
| 595 | function interactive_cli(r) |
| 596 | while not done do |
| 597 | pager_reset() |
| 598 | local cmd = r.readln() |
| 599 | if not cmd then |
| 600 | done = true |
| 601 | elseif cmd == "quit" or cmd == "exit" then |
| 602 | done = true |
| 603 | else |
| 604 | local func = device_cli_exec(r) |
| 605 | if func then |
| 606 | func(r) |
| 607 | else |
| 608 | if trim(cmd) == "" then |
| 609 | else |
| 610 | for i = 1, #r.prompt do |
| 611 | paged_write(" ") |
| 612 | end |
| 613 | paged_write("^\n% Invalid input detected at '^' marker.\n\n") |
| 614 | end |
| 615 | end |
| 616 | end |
| 617 | end |
| 618 | end |
| 619 | |
| 620 | device = {} |
| 621 | device.output = {} |
| 622 | |
| 623 | init_vpp(vpp) |
| 624 | cmds_str = run_cli(vpp, "?") |
| 625 | vpp_cmds = readln.split(cmds_str, "\n") |
| 626 | vpp_clis = {} |
| 627 | |
| 628 | for linenum, line in ipairs(vpp_cmds) do |
| 629 | local m,h = string.match(line, "^ (.-) (.*)$") |
| 630 | if m and #m > 0 then |
| 631 | table.insert(vpp_clis, m) |
| 632 | device.output["vpp debug cli " .. m] = function(rl) |
| 633 | -- print("ARBITRARY CLI" .. vpp.dump(rl.choices)) |
| 634 | print("LUACLI command: " .. table.concat(rl.choices, " ")) |
| 635 | local sub = {} |
| 636 | -- |
| 637 | for i=4, #rl.choices -1 do |
| 638 | table.insert(sub, rl.choices[i]) |
| 639 | end |
| 640 | local cli = table.concat(sub, " ") |
| 641 | print("Running CLI: " .. tostring(cli)) |
| 642 | paged_write(run_cli(vpp, cli)) |
| 643 | end |
| 644 | device.output["vpp debug cli " .. m .. " " .. dotdotdot] = function(rl) |
| 645 | print("ARGH") |
| 646 | end |
| 647 | |
| 648 | local ret = run_cli(vpp, "help " .. m) |
| 649 | device.output["help vpp debug cli " .. m] = { ret } |
| 650 | end |
| 651 | end |
| 652 | |
| 653 | for linenum, line in ipairs(vpp_clis) do |
| 654 | -- print(line, ret) |
| 655 | end |
| 656 | |
Andrew Yourtchenko | b868e4e | 2016-12-08 14:03:55 +0000 | [diff] [blame] | 657 | for msgnum, msgname in pairs(vpp.msg_number_to_name) do |
Andrew Yourtchenko | fa1456a | 2016-11-11 16:32:52 +0000 | [diff] [blame] | 658 | local cli, numspaces = string.gsub(msgname, "_", " ") |
| 659 | device.output["call " .. cli .. " " .. dotdotdot] = function(rl) |
| 660 | print("ARGH") |
| 661 | end |
| 662 | device.output["call " .. cli] = function(rl) |
| 663 | print("LUACLI command: " .. table.concat(rl.choices, " ")) |
| 664 | print("Running API: " .. msgname) -- vpp.dump(rl.choices)) |
| 665 | local out = {} |
| 666 | local args = {} |
| 667 | local ntaken = 0 |
| 668 | local argname = "" |
| 669 | for i=(1+1+numspaces+1), #rl.choices-1 do |
| 670 | -- print(i, rl.choices[i]) |
| 671 | if ntaken > 0 then |
| 672 | ntaken = ntaken -1 |
| 673 | else |
| 674 | local fieldname = rl.choices[i] |
| 675 | local field = vpp.msg_name_to_fields[msgname][fieldname] |
| 676 | if field then |
| 677 | local s = rl.choices[i+1] |
| 678 | s=s:gsub("\\x(%x%x)",function (x) return string.char(tonumber(x,16)) end) |
| 679 | args[fieldname] = s |
| 680 | ntaken = 1 |
| 681 | end |
| 682 | end |
| 683 | end |
| 684 | -- print("ARGS: ", vpp.dump(args)) |
| 685 | local ret = vpp:api_call(msgname, args) |
| 686 | for i, reply in ipairs(ret) do |
| 687 | table.insert(out, "=================== Entry #" .. tostring(i)) |
| 688 | for k, v in pairs(reply) do |
| 689 | table.insert(out, " " .. tostring(k) .. " : " .. toprintablestring(v)) |
| 690 | end |
| 691 | end |
| 692 | -- paged_write(vpp.dump(ret) .. "\n\n") |
| 693 | paged_write(table.concat(out, "\n").."\n\n") |
| 694 | end |
| 695 | device.output["call " .. cli .. " help"] = function(rl) |
| 696 | local out = {} |
| 697 | for k, v in pairs(vpp.msg_name_to_fields[msgname]) do |
| 698 | table.insert(out, tostring(k) .. " : " .. v["ctype"] .. " ; " .. tostring(vpp.dump(v)) ) |
| 699 | end |
| 700 | -- paged_write(vpp.dump(vpp.msg_name_to_fields[msgname]) .. "\n\n") |
| 701 | paged_write(table.concat(out, "\n").."\n\n") |
| 702 | end |
| 703 | -- vpp.msg_name_to_number = {} |
| 704 | end |
| 705 | |
| 706 | |
| 707 | |
| 708 | local r = readln.reader() |
| 709 | local done = false |
| 710 | |
| 711 | r.prompt = "VPP(luaCLI)#" |
| 712 | |
| 713 | r.help = device_cli_help |
| 714 | r.tab_complete = device_cli_tab_complete |
| 715 | print("===== CLI view, use ^D to end =====") |
| 716 | |
| 717 | r.tree = populate_tree(device.output) |
| 718 | -- readln.pretty("xxxx", r.tree) |
| 719 | |
| 720 | |
| 721 | for idx, an_arg in ipairs(arg) do |
| 722 | local fname = an_arg |
| 723 | if fname == "-i" then |
| 724 | pager_lines = 23 |
| 725 | interactive_cli(r) |
| 726 | else |
| 727 | pager_lines = 100000000 |
| 728 | for line in io.lines(fname) do |
| 729 | r.command = line |
| 730 | local func = device_cli_exec(r) |
| 731 | if func then |
| 732 | func(r) |
| 733 | end |
| 734 | end |
| 735 | end |
| 736 | end |
| 737 | |
| 738 | if #arg == 0 then |
| 739 | print("You should specify '-i' as an argument for the interactive session,") |
| 740 | print("but with no other sources of commands, we start interactive session now anyway") |
| 741 | interactive_cli(r) |
| 742 | end |
| 743 | |
| 744 | vpp:disconnect() |
| 745 | r.done() |
| 746 | |
| 747 | |