~ubuntu-branches/ubuntu/wily/marionnet/wily

« back to all changes in this revision

Viewing changes to switch.ml

  • Committer: Package Import Robot
  • Author(s): Lucas Nussbaum
  • Date: 2013-03-29 15:57:12 UTC
  • Revision ID: package-import@ubuntu.com-20130329155712-o0b9b96w8av68ktq
Tags: upstream-0.90.6+bzr407
ImportĀ upstreamĀ versionĀ 0.90.6+bzr407

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
(* This file is part of Marionnet, a virtual network laboratory
 
2
   Copyright (C) 2010  Jean-Vincent Loddo
 
3
   Copyright (C) 2010  UniversitĆ© Paris 13
 
4
 
 
5
   This program is free software: you can redistribute it and/or modify
 
6
   it under the terms of the GNU General Public License as published by
 
7
   the Free Software Foundation, either version 2 of the License, or
 
8
   (at your option) any later version.
 
9
 
 
10
   This program is distributed in the hope that it will be useful,
 
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
   GNU General Public License for more details.
 
14
 
 
15
   You should have received a copy of the GNU General Public License
 
16
   along with this program.  If not, see <http://www.gnu.org/licenses/>. *)
 
17
 
 
18
 
 
19
(** "Switch" component implementation. *)
 
20
 
 
21
#load "where_p4.cmo"
 
22
;;
 
23
 
 
24
open Gettext
 
25
 
 
26
(* Switch related constants: *)
 
27
(* TODO: make it configurable! *)
 
28
module Const = struct
 
29
 let port_no_default = 4
 
30
 let port_no_min = 4
 
31
 let port_no_max = 16
 
32
 
 
33
 let initial_content_for_rcfiles =
 
34
"# ===== FAST SPANNING TREE COMMANDS
 
35
# fstp/setfstp 0/1             Fast spanning tree protocol 1=ON 0=OFF
 
36
# fstp/setedge VLAN PORT 1/0   Define an edge port for a vlan 1=Y 0=N
 
37
# fstp/bonus   VLAN PORT COST  set the port bonus for a vlan
 
38
# ===== PORT STATUS COMMANDS
 
39
#Ā port/sethub  0/1             1=HUB 0=switch
 
40
# port/setvlan PORT VLAN       assign PORT to VLAN (untagged port)
 
41
# ===== VLAN MANAGEMENT COMMANDS
 
42
# vlan/create  VLAN            create the vlan VLAN
 
43
# vlan/remove  VLAN            remove the vlan VLAN
 
44
# vlan/addport VLAN PORT       add PORT to the VLAN's trunk (tagged)
 
45
# vlan/delport VLAN PORT       remove PORT from the VLAN's trunk
 
46
" ;;
 
47
 
 
48
end
 
49
 
 
50
(* The type of data exchanged with the dialog: *)
 
51
module Data = struct
 
52
type t = {
 
53
  name              : string;
 
54
  label             : string;
 
55
  port_no           : int;
 
56
  show_vde_terminal : bool;
 
57
  activate_fstp     : bool;
 
58
  rc_config         : bool * string;
 
59
  old_name          : string;
 
60
  }
 
61
 
 
62
let to_string t = "<obj>" (* TODO? *)
 
63
end (* Data *)
 
64
 
 
65
module Make_menus (Params : sig
 
66
  val st      : State.globalState
 
67
  val packing : [ `toolbar of GButton.toolbar | `menu_parent of Menu_factory.menu_parent ]
 
68
 end) = struct
 
69
 
 
70
  open Params
 
71
 
 
72
  module Toolbar_entry = struct
 
73
   let imagefile = "ico.switch.palette.png"
 
74
   let tooltip   = (s_ "Switch")
 
75
   let packing   = Params.packing
 
76
  end
 
77
 
 
78
  module Add = struct
 
79
    include Data
 
80
 
 
81
    let key = Some GdkKeysyms._S
 
82
 
 
83
    let ok_callback t = Gui_bricks.Ok_callback.check_name t.name t.old_name st#network#name_exists t
 
84
 
 
85
    let dialog () =
 
86
      let name = st#network#suggestedName "S" in
 
87
      Dialog_add_or_update.make ~title:(s_ "Add switch") ~name ~ok_callback ()
 
88
 
 
89
    let reaction
 
90
       { name = name; label = label; port_no = port_no;
 
91
         show_vde_terminal = show_vde_terminal; activate_fstp = activate_fstp;
 
92
         rc_config = rc_config; }
 
93
      =
 
94
      let action () =
 
95
        ignore
 
96
          (new User_level_switch.switch
 
97
                 ~network:st#network ~name ~label ~port_no ~show_vde_terminal ~activate_fstp ~rc_config ())
 
98
      in
 
99
      st#network_change action ();
 
100
 
 
101
  end
 
102
 
 
103
  module Properties = struct
 
104
    include Data
 
105
    let dynlist () = st#network#get_nodes_that_can_startup ~devkind:`Switch ()
 
106
 
 
107
    let dialog name () =
 
108
     let d = (st#network#get_node_by_name name) in
 
109
     let s = ((Obj.magic d):> User_level_switch.switch) in
 
110
     let title = (s_ "Modify switch")^" "^name in
 
111
     let label = s#get_label in
 
112
     let port_no = s#get_port_no in
 
113
     let port_no_min = st#network#port_no_lower_of (s :> User_level.node) in
 
114
     let show_vde_terminal = s#get_show_vde_terminal in
 
115
     let activate_fstp = s#get_activate_fstp in
 
116
     let rc_config = s#get_rc_config in
 
117
     Dialog_add_or_update.make
 
118
       ~title ~name ~label ~port_no ~port_no_min
 
119
       ~show_vde_terminal ~activate_fstp ~rc_config
 
120
       ~ok_callback:Add.ok_callback ()
 
121
 
 
122
    let reaction { name = name; label = label; port_no = port_no;
 
123
                   old_name = old_name;
 
124
                   show_vde_terminal = show_vde_terminal;
 
125
                   activate_fstp = activate_fstp;
 
126
                   rc_config = rc_config }
 
127
      =
 
128
      let d = (st#network#get_node_by_name old_name) in
 
129
      let s = ((Obj.magic d):> User_level_switch.switch) in
 
130
      let action () = s#update_switch_with ~name ~label ~port_no ~show_vde_terminal ~activate_fstp ~rc_config in
 
131
      st#network_change action ();
 
132
 
 
133
  end
 
134
 
 
135
  module Remove = struct
 
136
    type t = string (* just the name *)
 
137
    let to_string = (Printf.sprintf "name = %s\n")
 
138
 
 
139
    let dynlist = Properties.dynlist
 
140
 
 
141
    let dialog name () =
 
142
      Gui_bricks.Dialog.yes_or_cancel_question
 
143
        ~title:(s_ "Remove")
 
144
        ~markup:(Printf.sprintf (f_ "Are you sure that you want to remove %s\nand all the cables connected to this %s?") name (s_ "switch"))
 
145
        ~context:name
 
146
        ()
 
147
 
 
148
    let reaction name =
 
149
      let d = (st#network#get_node_by_name name) in
 
150
      let h = ((Obj.magic d):> User_level_switch.switch) in
 
151
      let action () = h#destroy in
 
152
      st#network_change action ();
 
153
 
 
154
  end
 
155
 
 
156
  module Startup = struct
 
157
    type t = string (* just the name *)
 
158
    let to_string = (Printf.sprintf "name = %s\n")
 
159
    let dynlist    = Properties.dynlist
 
160
    let dialog     = Menu_factory.no_dialog_but_simply_return_name
 
161
    let reaction name = (st#network#get_node_by_name name)#startup
 
162
 
 
163
  end
 
164
 
 
165
  module Stop = struct
 
166
    type t = string (* just the name *)
 
167
    let to_string = (Printf.sprintf "name = %s\n")
 
168
    let dynlist () = st#network#get_nodes_that_can_gracefully_shutdown ~devkind:`Switch ()
 
169
    let dialog = Menu_factory.no_dialog_but_simply_return_name
 
170
    let reaction name = (st#network#get_node_by_name name)#gracefully_shutdown
 
171
 
 
172
  end
 
173
 
 
174
  module Suspend = struct
 
175
    type t = string (* just the name *)
 
176
    let to_string = (Printf.sprintf "name = %s\n")
 
177
    let dynlist () = st#network#get_nodes_that_can_suspend ~devkind:`Switch ()
 
178
    let dialog = Menu_factory.no_dialog_but_simply_return_name
 
179
    let reaction name = (st#network#get_node_by_name name)#suspend
 
180
 
 
181
  end
 
182
 
 
183
  module Resume = struct
 
184
    type t = string (* just the name *)
 
185
    let to_string = (Printf.sprintf "name = %s\n")
 
186
    let dynlist () = st#network#get_nodes_that_can_resume ~devkind:`Switch ()
 
187
    let dialog = Menu_factory.no_dialog_but_simply_return_name
 
188
    let reaction name = (st#network#get_node_by_name name)#resume
 
189
 
 
190
  end
 
191
 
 
192
 module Create_entries =
 
193
  Gui_toolbar_COMPONENTS_layouts.Layout_for_network_node (Params) (Toolbar_entry) (Add) (Properties) (Remove) (Startup) (Stop) (Suspend) (Resume)
 
194
 
 
195
 (* Subscribe this kind of component to the network club: *)
 
196
 st#network#subscribe_a_try_to_add_procedure Eval_forest_child.try_to_add_switch;
 
197
 
 
198
end
 
199
 
 
200
(*-----*)
 
201
  WHERE
 
202
(*-----*)
 
203
 
 
204
module Dialog_add_or_update = struct
 
205
 
 
206
(* This function may be useful for testing the widget creation without
 
207
   recompiling the whole project. *)
 
208
let make
 
209
 ?(title="Add switch")
 
210
 ?(name="")
 
211
 ?label
 
212
 ?(port_no=Const.port_no_default)
 
213
 ?(port_no_min=Const.port_no_min)
 
214
 ?(port_no_max=Const.port_no_max)
 
215
 ?(show_vde_terminal=false)
 
216
 ?(activate_fstp=false)
 
217
 ?(rc_config=(false, Const.initial_content_for_rcfiles))
 
218
 ?(help_callback=help_callback) (* defined backward with "WHERE" *)
 
219
 ?(ok_callback=(fun data -> Some data))
 
220
 ?(dialog_image_file=Initialization.Path.images^"ico.switch.dialog.png")
 
221
 () :'result option =
 
222
  let old_name = name in
 
223
  let (w,_,name,label) =
 
224
    Gui_bricks.Dialog_add_or_update.make_window_image_name_and_label
 
225
      ~title
 
226
      ~image_file:dialog_image_file
 
227
      ~image_tooltip:(s_ "Switch")
 
228
      ~name
 
229
      ~name_tooltip:(s_ "Switch name. This name must be unique in the virtual network. Suggested: S1, S2, ...")
 
230
      ?label
 
231
      ()
 
232
  in
 
233
  let (port_no, show_vde_terminal, activate_fstp, rc_config) =
 
234
    let vbox = GPack.vbox ~homogeneous:false ~border_width:20 ~spacing:10 ~packing:w#vbox#add () in
 
235
    let form =
 
236
      Gui_bricks.make_form_with_labels
 
237
        ~packing:vbox#add
 
238
        [(s_ "Ports number");
 
239
         (s_ "Show VDE terminal");
 
240
         (s_ "Activate FSTP");
 
241
         (s_ "Startup configuration");
 
242
         ]
 
243
    in
 
244
    let port_no =
 
245
      Gui_bricks.spin_byte
 
246
        ~packing:(form#add_with_tooltip (s_ "Switch ports number"))
 
247
        ~lower:port_no_min ~upper:port_no_max ~step_incr:2
 
248
        port_no
 
249
    in
 
250
    let show_vde_terminal =
 
251
      GButton.check_button
 
252
        ~active:show_vde_terminal
 
253
        ~packing:(form#add_with_tooltip (s_ "Check to access the switch through a terminal" ))
 
254
        ()
 
255
    in
 
256
    let activate_fstp =
 
257
      GButton.check_button
 
258
        ~active:activate_fstp
 
259
        ~packing:(form#add_with_tooltip (s_ "Check to activate the FSTP (Fast Spanning Tree Protocol)" ))
 
260
        ()
 
261
    in
 
262
    let rc_config =
 
263
      let hbox = GPack.hbox
 
264
        ~packing:(form#add_with_tooltip (s_ "Check to activate a startup configuration" ))
 
265
        ~homogeneous:true
 
266
        ()
 
267
      in
 
268
      let check_button =
 
269
        GButton.check_button
 
270
          ~active:(fst rc_config)
 
271
          ~packing:(hbox#add)
 
272
          ()
 
273
      in
 
274
      let edit_button =
 
275
        GButton.button ~stock:`EDIT ~packing:hbox#add ()
 
276
      in
 
277
      let content = ref (snd rc_config) in
 
278
      let make_editing_window () =
 
279
        let result = Egg.create () in
 
280
        let () =
 
281
          Gui_source_editing.window
 
282
            ~title:(Printf.sprintf (f_ "%s configuration file") old_name)
 
283
            ~language:(`id "vde_switch")
 
284
            ~modal:()
 
285
            ~content:(!content)
 
286
            ~result
 
287
            ~create_as_dialog:()
 
288
            ~draw_spaces:[]
 
289
            ~position:`MOUSE
 
290
            ()
 
291
        in
 
292
        ignore (Thread.create (fun () -> content := Option.extract_or (Egg.wait result) !content) ());
 
293
      in
 
294
      ignore (edit_button#connect#clicked (make_editing_window));
 
295
      (edit_button#misc#set_sensitive check_button#active);
 
296
      ignore (check_button#connect#toggled (fun () -> edit_button#misc#set_sensitive check_button#active));
 
297
      object method active = check_button#active  method content = !content end
 
298
    in
 
299
    (port_no, show_vde_terminal, activate_fstp, rc_config)
 
300
  in
 
301
 
 
302
  let get_widget_data () :'result =
 
303
    let name = name#text in
 
304
    let label = label#text in
 
305
    let port_no = int_of_float port_no#value in
 
306
    let show_vde_terminal = show_vde_terminal#active in
 
307
    let rc_config = (rc_config#active, rc_config#content) in
 
308
    let activate_fstp = activate_fstp#active in
 
309
      { Data.name = name;
 
310
        Data.label = label;
 
311
        Data.port_no = port_no;
 
312
        Data.show_vde_terminal = show_vde_terminal;
 
313
        Data.activate_fstp = activate_fstp;
 
314
        Data.rc_config = rc_config;
 
315
        Data.old_name = old_name;
 
316
        }
 
317
  in
 
318
  (* The result of make is the result of the dialog loop (of type 'result option): *)
 
319
  Gui_bricks.Dialog_run.ok_or_cancel w ~ok_callback ~help_callback ~get_widget_data ()
 
320
 
 
321
(*-----*)
 
322
  WHERE
 
323
(*-----*)
 
324
 
 
325
 let help_callback =
 
326
   let title = (s_ "ADD OR MODIFY A SWITCH") in
 
327
   let msg   = (s_ "\
 
328
In this dialog window you can define the name of an Ethernet switch \
 
329
and set parameters for it:\n\n\
 
330
- Label: a string appearing near the switch icon in the network graph; it may \
 
331
allow, for example, to know at a glance the Ethernet network realized by the device; \
 
332
this field is exclusively for graphic purposes, is not taken in consideration \
 
333
for the configuration.\n\n\
 
334
- Nb of Ports: the number of ports of the switch (default 4); this number must \
 
335
not be increased without a reason, because the number of processes needed for the \
 
336
device emulation is proportional to his ports number.")
 
337
   in Simple_dialogs.help title msg
 
338
 
 
339
end
 
340
 
 
341
(*-----*)
 
342
  WHERE
 
343
(*-----*)
 
344
 
 
345
module Eval_forest_child = struct
 
346
 
 
347
 let try_to_add_switch (network:User_level.network) ((root,childs):Xforest.tree) =
 
348
  try
 
349
   (match root with
 
350
    | ("switch", attrs) ->
 
351
        let name  = List.assoc "name" attrs in
 
352
        let port_no = int_of_string (List.assoc "port_no" attrs) in
 
353
        Log.printf "Importing switch \"%s\" with %d ports...\n" name port_no;
 
354
        let x = new User_level_switch.switch ~network ~name ~port_no () in
 
355
        x#from_tree ("switch", attrs) childs;
 
356
        Log.printf "Switch \"%s\" successfully imported.\n" name;
 
357
        true
 
358
 
 
359
    (* backward compatibility *)
 
360
    | ("device", attrs) ->
 
361
        let name  = List.assoc "name" attrs in
 
362
        let port_no = try int_of_string (List.assoc "eth" attrs) with _ -> Const.port_no_default in
 
363
        let kind = List.assoc "kind" attrs in
 
364
        (match kind with
 
365
        | "switch" ->
 
366
            Log.printf "Importing switch \"%s\" with %d ports...\n" name port_no;
 
367
            let x = new User_level_switch.switch ~network ~name ~port_no () in
 
368
            x#from_tree ("device", attrs) childs; (* Just for the label... *)
 
369
            Log.printf "This is an old project: we set the user port offset to 1...\n";
 
370
            network#defects#change_port_user_offset ~device_name:name ~user_port_offset:1;
 
371
            Log.printf "Switch \"%s\" successfully imported.\n" name;
 
372
            true
 
373
        | _ -> false
 
374
        )
 
375
   | _ -> false
 
376
   )
 
377
  with _ -> false
 
378
 
 
379
end (* module Eval_forest_child *)
 
380
 
 
381
 
 
382
(*-----*)
 
383
  WHERE
 
384
(*-----*)
 
385
 
 
386
 
 
387
module User_level_switch = struct
 
388
 
 
389
class switch =
 
390
 
 
391
 fun ~network
 
392
     ~name
 
393
     ?label
 
394
     ~port_no
 
395
     ?(show_vde_terminal=false)
 
396
     ?(activate_fstp=false)
 
397
     ?(rc_config=(false,""))
 
398
     () ->
 
399
  object (self) inherit OoExtra.destroy_methods ()
 
400
 
 
401
  inherit
 
402
    User_level.node_with_ledgrid_and_defects
 
403
      ~network
 
404
      ~name ?label ~devkind:`Switch
 
405
      ~port_no
 
406
      ~port_no_min:Const.port_no_min
 
407
      ~port_no_max:Const.port_no_max
 
408
      ~user_port_offset:1 (* in order to have a perfect mapping with VDE *)
 
409
      ~port_prefix:"port"
 
410
      ()
 
411
    as self_as_node_with_ledgrid_and_defects
 
412
  method ledgrid_label = "Switch"
 
413
  method defects_device_type = "switch"
 
414
  method polarity = User_level.MDI_X
 
415
  method string_of_devkind = "switch"
 
416
 
 
417
  val mutable show_vde_terminal : bool = show_vde_terminal
 
418
  method get_show_vde_terminal = show_vde_terminal
 
419
  method set_show_vde_terminal x = show_vde_terminal <- x
 
420
 
 
421
  val mutable activate_fstp : bool = activate_fstp
 
422
  method get_activate_fstp  = activate_fstp
 
423
  method set_activate_fstp x = activate_fstp <- x
 
424
 
 
425
  val mutable rc_config : bool * string  = rc_config
 
426
  method get_rc_config = rc_config
 
427
  method set_rc_config x = rc_config <- x
 
428
 
 
429
  method dotImg iconsize =
 
430
   let imgDir = Initialization.Path.images in
 
431
   (imgDir^"ico.switch."^(self#string_of_simulated_device_state)^"."^iconsize^".png")
 
432
 
 
433
  method update_switch_with ~name ~label ~port_no
 
434
   ~show_vde_terminal ~activate_fstp ~rc_config
 
435
   =
 
436
   (* The following call ensure that the simulated device will be destroyed: *)
 
437
   self_as_node_with_ledgrid_and_defects#update_with ~name ~label ~port_no;
 
438
   self#set_show_vde_terminal (show_vde_terminal);
 
439
   self#set_activate_fstp (activate_fstp);
 
440
   self#set_rc_config (rc_config);
 
441
 
 
442
  (** Create the simulated device *)
 
443
  method private make_simulated_device =
 
444
    let hublet_no = self#get_port_no in
 
445
    let show_vde_terminal = self#get_show_vde_terminal in
 
446
    let fstp = Option.of_bool (self#get_activate_fstp) in
 
447
(*  let rcfile =
 
448
      match self#get_rc_config with
 
449
      | false, _ -> None
 
450
      | true, content ->
 
451
          let filename =
 
452
            let motherboard = Motherboard.extract () in
 
453
            UnixExtra.temp_file
 
454
              ~parent:motherboard#project_working_directory
 
455
              ~prefix:(Printf.sprintf "switch_%s_rcfile." self#get_name)
 
456
              ~content
 
457
              ()
 
458
          in
 
459
          self#add_destroy_callback (lazy (Unix.unlink filename));
 
460
          Some filename
 
461
    in*)
 
462
    let rcfile_content =
 
463
      match self#get_rc_config with
 
464
      | false, _ -> None
 
465
      | true, content -> Some content
 
466
    in
 
467
    let unexpected_death_callback = self#destroy_because_of_unexpected_death in
 
468
    ((new Simulation_level_switch.switch
 
469
       ~parent:self
 
470
       ~hublet_no          (* TODO: why not accessible from parent? *)
 
471
       ~show_vde_terminal  (* TODO: why not accessible from parent? *)
 
472
       ?fstp
 
473
       ?rcfile_content
 
474
       ~unexpected_death_callback
 
475
       ()) :> User_level.node Simulation_level.device)
 
476
 
 
477
  method to_tree =
 
478
   Forest.tree_of_leaf ("switch", [
 
479
      ("name"     ,  self#get_name );
 
480
      ("label"    ,  self#get_label);
 
481
      ("port_no"  ,  (string_of_int self#get_port_no))  ;
 
482
      ("show_vde_terminal" , string_of_bool (self#get_show_vde_terminal));
 
483
      ("activate_fstp"     , string_of_bool (self#get_activate_fstp));
 
484
      ("rc_config"         , Marshal.to_string self#get_rc_config []);
 
485
      ])
 
486
 
 
487
  method eval_forest_attribute = function
 
488
  | ("name"     , x ) -> self#set_name x
 
489
  | ("label"    , x ) -> self#set_label x
 
490
  | ("port_no"  , x ) -> self#set_port_no (int_of_string x)
 
491
  | ("show_vde_terminal", x ) -> self#set_show_vde_terminal (bool_of_string x)
 
492
  | ("activate_fstp", x )     -> self#set_activate_fstp (bool_of_string x)
 
493
  | ("rc_config", x )         -> self#set_rc_config (Marshal.from_string x 0)
 
494
  | _ -> () (* Forward-comp. *)
 
495
 
 
496
end (* class switch *)
 
497
 
 
498
end (* module User_level *)
 
499
 
 
500
(*-----*)
 
501
  WHERE
 
502
(*-----*)
 
503
 
 
504
module Simulation_level_switch = struct
 
505
 
 
506
(* The question is "port/print" *)
 
507
let scan_vde_switch_answer_to_port_print (ch:Network.stream_channel) : int =
 
508
  let rec loop n =
 
509
    let answer = ch#input_line () in
 
510
    try (Scanf.sscanf answer "Port %d %s ACTIVE") (fun i _ -> ()); loop (n+1) with _ ->
 
511
    try (Scanf.sscanf answer ".") (); n with _ -> loop n
 
512
  in
 
513
  loop 0
 
514
 
 
515
let ask_vde_switch_for_current_active_ports ~socketfile () =
 
516
  let protocol (ch:Network.stream_channel) =
 
517
    ch#output_line "port/print";
 
518
    scan_vde_switch_answer_to_port_print ch
 
519
  in
 
520
  Network.stream_unix_client ~socketfile ~protocol ()
 
521
 
 
522
let wait_vde_switch_until_ports_will_be_allocated ~numports ~socketfile () =
 
523
  let rec protocol (ch:Network.stream_channel) =
 
524
    ch#output_line "port/print";
 
525
    let active_ports = scan_vde_switch_answer_to_port_print ch in
 
526
    if active_ports >= numports then active_ports else (Thread.delay 0.2; (protocol ch))
 
527
  in
 
528
  Network.stream_unix_client ~socketfile ~protocol ()
 
529
 
 
530
(*let send_commands_to_vde_switch ~socketfile ~commands () =
 
531
  Log.printf "Sending commands to a switch:\n---\n%s\n---\n" commands;
 
532
  let protocol (ch:Network.stream_channel) = ch#send commands in
 
533
  Network.stream_unix_client ~socketfile ~protocol ()*)
 
534
 
 
535
let get_lines_removing_comments (commands:string) : string list =
 
536
  let t = StringExtra.Text.of_string commands in
 
537
  let result = StringExtra.Text.grep (Str.regexp "^[^#]") t in
 
538
  result
 
539
 
 
540
(* Currently unused, but useful for testing: *)
 
541
let get_vde_switch_boolean_answer (ch:Network.stream_channel) : bool =
 
542
  let ignore2 _ _ = () in
 
543
  let rec loop () =
 
544
    Log.printf "Waiting for an answer...\n";
 
545
    let answer = ch#input_line () in
 
546
    Log.printf "Received answer `%s'\n" answer;
 
547
    try (Scanf.sscanf answer "vde$ 1000 Success" ()); true with _ ->
 
548
    try (Scanf.sscanf answer "vde$ %d %s" ignore2); false with _ ->
 
549
    loop ()
 
550
  in
 
551
  loop ()
 
552
 
 
553
(* Currently unused, but useful for testing: *)
 
554
let send_commands_to_vde_switch_and_get_answers ~socketfile ~commands ()
 
555
  : (exn, (string * bool) list) Either.t
 
556
  =
 
557
  let lines = get_lines_removing_comments commands in
 
558
  Log.printf "Sending commands to a switch:\n---\n%s\n---\n" commands;
 
559
  let protocol (ch:Network.stream_channel) =
 
560
    List.map
 
561
       (fun line ->
 
562
          Log.printf "Sending line: %s\n" line;
 
563
          ch#output_line line;
 
564
          let answer = get_vde_switch_boolean_answer ch in
 
565
          Log.printf "Received boolean answer: %b\n" (answer);
 
566
          (line, answer))
 
567
       lines
 
568
  in
 
569
  Network.stream_unix_client ~socketfile ~protocol ()
 
570
 
 
571
let rec repeat_until_exception f x =
 
572
 try ignore (f x); repeat_until_exception f x with _ -> ()
 
573
 
 
574
let send_commands_to_vde_switch_ignoring_answers ~socketfile ~commands () =
 
575
  let lines = get_lines_removing_comments commands in
 
576
  let protocol (ch:Network.stream_channel) =
 
577
    ignore (Thread.create (repeat_until_exception ch#input_line) ());
 
578
    List.iter
 
579
       (fun line ->
 
580
          Log.printf "Sending line: %s\n" line;
 
581
          ch#output_line line;
 
582
          Thread.delay 0.01;
 
583
          ())
 
584
       lines
 
585
  in
 
586
  Network.stream_unix_client ~socketfile ~protocol ()
 
587
 
 
588
 
 
589
(** A switch: just a [hub_or_switch] with [hub = false] *)
 
590
class ['parent] switch =
 
591
  fun ~(parent:'parent)
 
592
      ~hublet_no
 
593
      ?(last_user_visible_port_index:int option)
 
594
      ?(show_vde_terminal=false)
 
595
      ?fstp
 
596
      ?rcfile (* Unused: vde_switch doesn't interpret correctly commands provided in this way! *)
 
597
      ?rcfile_content
 
598
      ~unexpected_death_callback
 
599
      () ->
 
600
object(self)
 
601
  inherit ['parent] Simulation_level.hub_or_switch
 
602
      ~parent
 
603
      ~hublet_no
 
604
      ?last_user_visible_port_index
 
605
      ~hub:false
 
606
      ~management_socket:()
 
607
      ?fstp
 
608
      ?rcfile
 
609
      ~unexpected_death_callback
 
610
      ()
 
611
      as super
 
612
  method device_type = "switch"
 
613
 
 
614
  method spawn_internal_cables =
 
615
    match show_vde_terminal || (rcfile_content <> None) with
 
616
    | false -> super#spawn_internal_cables
 
617
    | true ->
 
618
        (* If the user want to configure VLANs etc, we must be sure that
 
619
           the port numbering will be the same for marionnet and vde_switch: *)
 
620
        let socketfile = Option.extract self#get_management_socket_name in
 
621
        let numports = ref (Either.extract (ask_vde_switch_for_current_active_ports ~socketfile ())) in
 
622
        let name = parent#get_name in
 
623
        Log.printf "The vde_switch %s has currently %d active ports.\n" name !numports;
 
624
        Log.printf "Spawning internal cables for switch %s...\n" name;
 
625
        List.iter (fun thunk -> thunk ())
 
626
          (List.map (* Here map returns a list of thunks *)
 
627
             begin fun internal_cable_process () ->
 
628
               (* The protocol implemented here should ensure that vde_switch will not be solicited
 
629
                  before having accepted the previously asked connection. However, for safety we add
 
630
                  a little delay in order to give to vde_switch the time to allocate the previous port: *)
 
631
               Thread.delay 0.1;
 
632
               (* Now we launch the process that will ask vde_switch to obtain a new port: *)
 
633
               internal_cable_process#spawn;
 
634
               incr numports;
 
635
               let answer =
 
636
                 Either.extract (wait_vde_switch_until_ports_will_be_allocated ~numports:(!numports) ~socketfile ())
 
637
               in
 
638
               (if answer <> !numports then Log.printf "Unexpected vde_switch %s answer: %d instead of the expected value %d. Ignoring.\n" name answer !numports);
 
639
               Log.printf "Ok, the vde_switch %s has now %d allocated ports.\n" name !numports;
 
640
               end
 
641
             self#get_internal_cable_processes);
 
642
        (* Now send rc commands to the switch: *)
 
643
        match rcfile_content with
 
644
        | None -> ()
 
645
        | Some commands ->
 
646
            ignore
 
647
              (Thread.create
 
648
                 (send_commands_to_vde_switch_ignoring_answers ~socketfile ~commands)              ())
 
649
 
 
650
 
 
651
  initializer
 
652
 
 
653
  match show_vde_terminal with
 
654
  | false -> ()
 
655
  | true ->
 
656
    let name = parent#get_name in
 
657
    self#add_accessory_process
 
658
      (new Simulation_level.unixterm_process
 
659
        ~xterm_title:(name^" terminal")
 
660
        ~management_socket_name:(Option.extract self#get_management_socket_name)
 
661
        ~unexpected_death_callback:
 
662
           (fun i _ ->
 
663
              Death_monitor.stop_monitoring i;
 
664
              Log.printf "Terminal of switch %s closed (pid %d).\n" name i)
 
665
        ())
 
666
 
 
667
end;;
 
668
 
 
669
end (* module Simulation_level_switch *)
 
670
 
 
671
(** Just for testing: *)
 
672
let test = Dialog_add_or_update.make