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

« back to all changes in this revision

Viewing changes to treeview_ifconfig.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) 2007, 2008  Luca Saiu
 
3
   Copyright (C) 2009, 2010  Jean-Vincent Loddo
 
4
   Copyright (C) 2007, 2008, 2009, 2010  UniversitĆ© Paris 13
 
5
 
 
6
   This program is free software: you can redistribute it and/or modify
 
7
   it under the terms of the GNU General Public License as published by
 
8
   the Free Software Foundation, either version 2 of the License, or
 
9
   (at your option) any later version.
 
10
 
 
11
   This program is distributed in the hope that it will be useful,
 
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
   GNU General Public License for more details.
 
15
 
 
16
   You should have received a copy of the GNU General Public License
 
17
   along with this program.  If not, see <http://www.gnu.org/licenses/>. *)
 
18
 
 
19
open Gettext;;
 
20
module Row_item = Treeview.Row_item ;;
 
21
module Row = Treeview.Row ;;
 
22
 
 
23
type port_row_completions = (string * (string * Row_item.t) list) list
 
24
 
 
25
class t =
 
26
fun ~packing
 
27
    ~after_user_edit_callback
 
28
    () ->
 
29
object(self)
 
30
  inherit
 
31
    Treeview.treeview_with_a_primary_key_Name_column
 
32
      ~packing
 
33
      ~hide_reserved_fields:true
 
34
      ()
 
35
  as super
 
36
 
 
37
  val uneditable_header = "_uneditable"
 
38
  method get_row_uneditable = self#get_CheckBox_field (uneditable_header)
 
39
 
 
40
  val type_header = "Type"
 
41
  method get_row_type = self#get_Icon_field (type_header)
 
42
  method set_row_type = self#set_Icon_field (type_header)
 
43
 
 
44
  val mac_address_header = "MAC address"
 
45
  method get_row_mac_address = self#get_String_field (mac_address_header)
 
46
  method set_row_mac_address = self#set_String_field (mac_address_header)
 
47
 
 
48
  val mtu_header = "MTU"
 
49
  method get_row_mtu = self#get_String_field (mtu_header)
 
50
  method set_row_mtu = self#set_String_field (mtu_header)
 
51
 
 
52
  val ipv4_address_header = "IPv4 address"
 
53
  method get_row_ipv4_address = self#get_String_field (ipv4_address_header)
 
54
  method set_row_ipv4_address = self#set_String_field (ipv4_address_header)
 
55
 
 
56
  val ipv4_broadcast_header = "IPv4 broadcast"
 
57
  method get_row_ipv4_broadcast = self#get_String_field (ipv4_broadcast_header)
 
58
  method set_row_ipv4_broadcast = self#set_String_field (ipv4_broadcast_header)
 
59
 
 
60
  val ipv4_netmask_header = "IPv4 netmask"
 
61
  method get_row_ipv4_netmask = self#get_String_field (ipv4_netmask_header)
 
62
  method set_row_ipv4_netmask = self#set_String_field (ipv4_netmask_header)
 
63
 
 
64
  val ipv6_address_header = "IPv6 address"
 
65
  method get_row_ipv6_address = self#get_String_field (ipv6_address_header)
 
66
  method set_row_ipv6_address = self#set_String_field (ipv6_address_header)
 
67
 
 
68
  method private currently_used_mac_addresses : string list =
 
69
    let xs = List.flatten (Forest.to_list self#get_forest) in
 
70
    let xs = ListExtra.filter_map
 
71
      (function
 
72
       | header, (Row_item.String s) when header=mac_address_header -> Some s
 
73
       | _ -> None
 
74
       )
 
75
       xs
 
76
    in
 
77
    (List.tl xs) (* Discard the first line (header) *)
 
78
 
 
79
  (** The three leftmost octects are used as the trailing part of
 
80
      automatically-generated MAC addresses.
 
81
      Interesting side note: we can't use four because of OCaml
 
82
      runtime type tagging (yes, Jean: I was also surprised when I
 
83
      discovered it, but it was made that way to support precise GC,
 
84
      which can't rely on conservative pointer finding). *)
 
85
  method private generate_mac_address =
 
86
    let b0 = Random.int 256 in
 
87
    let b1 = Random.int 256 in
 
88
    let b2 = Random.int 256 in
 
89
    let result = Printf.sprintf "02:04:06:%02x:%02x:%02x" b2 b1 b0 in
 
90
    (* Try again if we generated an invalid or already allocated address: *)
 
91
    if not (List.mem result self#currently_used_mac_addresses) then
 
92
      begin
 
93
        Log.printf "Generated MAC address: %s\n" result;
 
94
        result
 
95
      end
 
96
    else begin
 
97
      Log.printf "Generated MAC address: %s already in use!\n" result;
 
98
      self#generate_mac_address
 
99
    end
 
100
  (** This follows exactly the same logic as automatic MAC address generation.
 
101
      Two octects are used for a B class network: *)
 
102
  val next_ipv4_address_as_int =
 
103
    ref 1
 
104
  method private generate_ipv4_address =
 
105
    let ipv4_address_as_int = !next_ipv4_address_as_int in
 
106
    next_ipv4_address_as_int := ipv4_address_as_int + 1;
 
107
    let result =
 
108
      Printf.sprintf
 
109
        "10.10.%i.%i"
 
110
        (ipv4_address_as_int / 256)
 
111
        (ipv4_address_as_int mod 256)
 
112
    in
 
113
    (* Try again if we generated an invalid address: *)
 
114
    if Ipv4.String.is_valid_ipv4 result then
 
115
      result
 
116
    else
 
117
      self#generate_ipv4_address
 
118
 
 
119
  (** This follows exactly the same logic as automatic MAC address generation.
 
120
      Two octects are used for a B class network: *)
 
121
  val next_ipv6_address_as_int =
 
122
    ref Int64.one
 
123
  method private generate_ipv6_address =
 
124
    let ipv6_address_as_int = !next_ipv6_address_as_int in
 
125
    next_ipv6_address_as_int := Int64.succ ipv6_address_as_int;
 
126
    let result =
 
127
      Printf.sprintf
 
128
        "42::%04x:%04x"
 
129
        (Int64.to_int (Int64.div ipv6_address_as_int (Int64.of_int (256 * 256))))
 
130
        (Int64.to_int (Int64.rem ipv6_address_as_int (Int64.of_int (256 * 256))))
 
131
    in
 
132
    (* Try again if we generated an invalid address: *)
 
133
    if self#is_a_valid_ipv6_address result then
 
134
      result
 
135
    else
 
136
      self#generate_ipv6_address
 
137
 
 
138
  method add_device ?port_row_completions device_name device_type port_no =
 
139
    let row_id =
 
140
      self#add_row
 
141
        [ name_header,           Row_item.String device_name;
 
142
          type_header,           Row_item.Icon device_type;
 
143
          uneditable_header,     Row_item.CheckBox true;
 
144
          mtu_header,            Row_item.String "";
 
145
          mac_address_header,    Row_item.String "";
 
146
          ipv4_address_header,   Row_item.String "";
 
147
          ipv4_netmask_header,   Row_item.String "";
 
148
          ipv4_broadcast_header, Row_item.String "";
 
149
          ipv6_address_header,   Row_item.String "";
 
150
         ]
 
151
    in
 
152
    self#update_port_no ?port_row_completions device_name port_no;
 
153
    self#collapse_row row_id;
 
154
 
 
155
  method port_no_of ~device_name =
 
156
    self#children_no_of ~parent_name:device_name
 
157
 
 
158
  method private add_port ?port_row_completions device_name =
 
159
    let device_row_id = self#unique_row_id_of_name device_name in
 
160
    let current_port_no =
 
161
      self#port_no_of device_name in
 
162
    let port_type =
 
163
      match self#get_row_type (device_row_id) with
 
164
      | "machine" | "world_bridge" -> "machine-port"
 
165
      | "gateway" (* retro-compatibility *) -> "machine-port"
 
166
      | "router"             -> "router-port"
 
167
      | _                    -> "other-device-port" in
 
168
    let port_prefix =
 
169
      match self#get_row_type (device_row_id) with
 
170
        "machine" | "world_bridge" -> "eth"
 
171
      | "gateway" (* retro-compatibility *) -> "eth"
 
172
      | _ -> "port"
 
173
    in
 
174
    let port_name = (Printf.sprintf "%s%i" port_prefix current_port_no) in
 
175
    let port_row_standard =
 
176
      [ name_header, Row_item.String port_name;
 
177
        type_header, Row_item.Icon port_type; ]
 
178
    in
 
179
    let port_row = match port_row_completions with
 
180
      | None     -> port_row_standard
 
181
      | Some lst ->
 
182
         (try
 
183
           let port_row_specific_settings = (List.assoc port_name lst) in
 
184
           List.append (port_row_standard) (port_row_specific_settings)
 
185
          with Not_found -> port_row_standard)
 
186
    in
 
187
    ignore (self#add_row ~parent_row_id:device_row_id port_row)
 
188
 
 
189
  method update_port_no ?port_row_completions device_name new_port_no =
 
190
    let add_child_of = self#add_port ?port_row_completions in
 
191
    self#update_children_no ~add_child_of ~parent_name:device_name new_port_no
 
192
 
 
193
  (* To do: these validation methods suck. *)
 
194
  method private is_a_valid_mac_address address =
 
195
    try
 
196
      Scanf.sscanf
 
197
        address
 
198
        "%x:%x:%x:%x:%x:%x"
 
199
        (fun _ _ _ _ _ _ -> Scanf.sscanf address "%c%c:%c%c:%c%c:%c%c:%c%c:%c%c"
 
200
                                         (fun _ _ _ _ _ _ _ _ _ _ _ _ -> true))
 
201
    with _ ->
 
202
      false
 
203
 
 
204
  (* TODO: FIX IT: the validity depends on the ip and netmask (broadcast must belong the network addresses range). *)
 
205
  method private is_a_valid_ipv4_broadcast x =
 
206
(*    self#is_a_valid_ipv4_address x*)
 
207
   Ipv4.String.is_valid_ipv4 x
 
208
 
 
209
  method private is_a_valid_ipv6_address address =
 
210
    true
 
211
    (* This heuristic sucked *too* much. It's better to just accept everything. *)
 
212
    (*try
 
213
      Scanf.sscanf
 
214
        address
 
215
        "%x:%x:%x:%x:%x:%x:%x:%x"
 
216
        (fun o1 o2 o3 o4 o5 o6 o7 o8 ->
 
217
           o1 < 65536 && o2 < 65536 && o3 < 65536 && o4 < 65536 &&
 
218
           o5 < 65536 && o6 < 65536 && o7 < 65536 && o8 < 65536)
 
219
    with _ ->
 
220
      false *)
 
221
  method private is_a_valid_ipv6_netmask   x = self#is_a_valid_ipv6_address x
 
222
  method private is_a_valid_ipv6_broadcast x = self#is_a_valid_ipv6_address x
 
223
 
 
224
  method private is_a_valid_mtu x =
 
225
    if x = "" then
 
226
      true
 
227
    else try
 
228
      (int_of_string x) >= 0 && (int_of_string x) < 65537
 
229
    with _ ->
 
230
      false
 
231
 
 
232
  method get_port_data ~device_name ~port_name =
 
233
    self#get_row_of_child ~parent_name:device_name ~child_name:port_name
 
234
 
 
235
  (** Return all the non-reserved data of a given port *index* (for example
 
236
      2 stands for "eth2" or "port2", in our usual <name, item> alist
 
237
      format: *)
 
238
  (* TODO: remove it *)
 
239
  method get_port_data_by_index ~device_name ~port_index =
 
240
    (* First try with the "eth" prefix: *)
 
241
    let port_name = Printf.sprintf "eth%i" port_index in
 
242
    try
 
243
      self#get_port_data device_name port_name
 
244
    with _ ->
 
245
      (* We failed. Ok, now try with the "port" prefix, before bailing out: *)
 
246
      let port_name = Printf.sprintf "port%i" port_index in
 
247
      self#get_port_data ~device_name ~port_name
 
248
 
 
249
  (** Return a single port attribute as an item: *)
 
250
 
 
251
  method get_port_attribute ~device_name ~port_name ~field =
 
252
    let row = (self#get_port_data ~device_name ~port_name) in
 
253
    (Row.String_field.get ~field row)
 
254
 
 
255
  (** Return a single port attribute as an item: *)
 
256
  (* TODO: remove it and remove also get_port_data_by_index *)
 
257
  method get_port_attribute_by_index ~device_name ~port_index ~field =
 
258
    let row = (self#get_port_data_by_index ~device_name ~port_index) in
 
259
    (Row.String_field.get ~field row)
 
260
 
 
261
  (** Update a single port attribute: *)
 
262
  method set_port_attribute_by_index ~device_name ~port_index ~field value =
 
263
    let port_name = Printf.sprintf "port%i" port_index in
 
264
    let row =
 
265
      self#get_complete_row_of_child
 
266
        ~parent_name:device_name
 
267
        ~child_name:port_name
 
268
    in
 
269
    let row_id = Row.get_id row in
 
270
    self#set_row_field row_id field value;
 
271
 
 
272
  (** Update a single port attribute of type string: *)
 
273
  method set_port_string_attribute_by_index ~device_name ~port_index ~field value =
 
274
    self#set_port_attribute_by_index ~device_name ~port_index ~field (Row_item.String value)
 
275
 
 
276
  (** Clear the interface and set the full internal state back to its initial value: *)
 
277
  method clear =
 
278
    super#clear;
 
279
    next_ipv4_address_as_int := 1;
 
280
    next_ipv6_address_as_int := Int64.one
 
281
 
 
282
  val counters_marshaler = new Oomarshal.marshaller
 
283
 
 
284
  method save ?with_forest_treatment () =
 
285
    (* Save the forest, as usual: *)
 
286
    super#save ?with_forest_treatment ();
 
287
    (* ...but also save the counters used for generating fresh addresses: *)
 
288
    let counters_file_name = (Option.extract filename#get)^"-counters" in
 
289
    (* For forward compatibility: *)
 
290
    let _OBSOLETE_mac_address_as_int = Random.int (256*256*256) in
 
291
    counters_marshaler#to_file
 
292
      (_OBSOLETE_mac_address_as_int, !next_ipv4_address_as_int, !next_ipv6_address_as_int)
 
293
      counters_file_name;
 
294
 
 
295
  method load =
 
296
    (* Load the forest, as usual: *)
 
297
    super#load;
 
298
    (* ...but also load the counters used for generating fresh addresses: *)
 
299
    let counters_file_name = (Option.extract filename#get)^"-counters" in
 
300
    (* _OBSOLETE_mac_address_as_int read for backward compatibility: *)
 
301
    let _OBSOLETE_mac_address_as_int, the_next_ipv4_address_as_int, the_next_ipv6_address_as_int =
 
302
      counters_marshaler#from_file counters_file_name in
 
303
    next_ipv4_address_as_int := the_next_ipv4_address_as_int;
 
304
    next_ipv6_address_as_int := the_next_ipv6_address_as_int
 
305
 
 
306
  initializer
 
307
    let _ =
 
308
      self#add_checkbox_column
 
309
        ~header:uneditable_header
 
310
        ~hidden:true
 
311
        ~default:(fun () -> Row_item.CheckBox false)
 
312
        () in
 
313
    let _ =
 
314
      self#add_icon_column
 
315
        ~header:type_header
 
316
        ~shown_header:(s_ "Type")
 
317
        ~strings_and_pixbufs:[
 
318
           "machine", Initialization.Path.images^"treeview-icons/machine.xpm";
 
319
           "router",  Initialization.Path.images^"treeview-icons/router.xpm";
 
320
           "machine-port", Initialization.Path.images^"treeview-icons/network-card.xpm";
 
321
           "router-port",  Initialization.Path.images^"treeview-icons/port.xpm";
 
322
           "other-device-port", Initialization.Path.images^"treeview-icons/port.xpm";
 
323
            ]
 
324
        () in
 
325
    let _ =
 
326
      self#add_editable_string_column
 
327
        ~header:mac_address_header
 
328
        ~shown_header:(s_ "MAC address")
 
329
        ~default:(fun () -> Row_item.String self#generate_mac_address)
 
330
        ~constraint_predicate:(fun i -> let s = Row_item.extract_String i in
 
331
                                          (self#is_a_valid_mac_address s) or s = "")
 
332
        () in
 
333
    let _ =
 
334
      self#add_editable_string_column
 
335
        ~header:mtu_header
 
336
        ~default:(fun () -> Row_item.String "1500")
 
337
        ~constraint_predicate:(fun i -> let s = Row_item.extract_String i in
 
338
                                          (self#is_a_valid_mtu s) or s = "")
 
339
        () in
 
340
    let _ =
 
341
      self#add_editable_string_column
 
342
        ~header:ipv4_address_header
 
343
        ~shown_header:(s_ "IPv4 address")
 
344
        ~default:(fun () ->
 
345
                    if Global_options.get_autogenerate_ip_addresses () then
 
346
                      Row_item.String self#generate_ipv4_address
 
347
                    else
 
348
                      Row_item.String "")
 
349
        ~constraint_predicate:(fun i -> let s = Row_item.extract_String i in
 
350
                                          (Ipv4.String.is_valid_ipv4 s) or s = "")
 
351
        () in
 
352
    let _ =
 
353
      self#add_editable_string_column
 
354
        ~header:ipv4_broadcast_header
 
355
        ~shown_header:(s_ "IPv4 broadcast")
 
356
        ~default:(fun () ->
 
357
                    if Global_options.get_autogenerate_ip_addresses () then
 
358
                      Row_item.String "10.10.255.255"
 
359
                    else
 
360
                      Row_item.String "")
 
361
        ~constraint_predicate:(fun i -> let s = Row_item.extract_String i in
 
362
                                          (self#is_a_valid_ipv4_broadcast s) or s = "")
 
363
        () in
 
364
    let _ =
 
365
      self#add_editable_string_column
 
366
        ~header:ipv4_netmask_header
 
367
        ~shown_header:(s_ "IPv4 netmask")
 
368
        ~default:(fun () ->
 
369
                    if Global_options.get_autogenerate_ip_addresses () then
 
370
                      Row_item.String "255.255.0.0"
 
371
                    else
 
372
                      Row_item.String "")
 
373
        ~constraint_predicate:(fun i -> let s = Row_item.extract_String i in
 
374
                                          (Ipv4.String.is_valid_netmask s) or s = "")
 
375
        () in
 
376
    let _ =
 
377
      self#add_editable_string_column
 
378
        ~header:ipv6_address_header
 
379
        ~shown_header:(s_ "IPv6 address")
 
380
        ~default:(fun () ->
 
381
                    if Global_options.get_autogenerate_ip_addresses () then
 
382
                      Row_item.String self#generate_ipv6_address
 
383
                    else
 
384
                      Row_item.String "")
 
385
        ~constraint_predicate:(fun i -> let s = Row_item.extract_String i in
 
386
                                          (self#is_a_valid_ipv6_address s) or s = "")
 
387
        () in
 
388
 
 
389
 
 
390
  self#add_row_constraint
 
391
    ~name:(s_ "you should choose a port to define this parameter")
 
392
    (fun row ->
 
393
      let uneditable = Row.CheckBox_field.get ~field:uneditable_header row in
 
394
      (not uneditable) or
 
395
      (List.for_all (fun (name, value) ->
 
396
                       name = name_header or
 
397
                       name = type_header or
 
398
                       name = uneditable_header or
 
399
                       self#is_column_reserved name or
 
400
                       value = Row_item.String "")
 
401
                    row));
 
402
 
 
403
  self#add_row_constraint
 
404
    ~name:(s_ "the router first port must always have a valid configuration address")
 
405
    (fun row ->
 
406
      let port_name = (Row.get_name row) in
 
407
      let port_type = (Row.Icon_field.get ~field:type_header row) in
 
408
      let address   = (Row.String_field.get ~field:ipv4_address_header row) in
 
409
      let netmask   = (Row.String_field.get ~field:ipv4_netmask_header row) in
 
410
      (port_name <> "port0") or
 
411
      (port_type <> "router-port") or
 
412
      ((address <> "") && (netmask <> "")));
 
413
 
 
414
    (* In this treeview the involved device is the parent: *)
 
415
    self#set_after_update_callback
 
416
      (fun row_id ->
 
417
        after_user_edit_callback (self#get_row_parent_name row_id));
 
418
 
 
419
    (* Make internal data structures: no more columns can be added now: *)
 
420
    self#create_store_and_view;
 
421
 
 
422
    (* Setup the contextual menu: *)
 
423
    self#set_contextual_menu_title "Network interface's configuration";
 
424
end;;
 
425
 
 
426
(** Ugly kludge to make a single global instance visible from all modules
 
427
    linked *after* this one. Not having mutually-recursive inter-compilation-unit
 
428
    modules is a real pain. *)
 
429
 
 
430
class treeview = t
 
431
module The_unique_treeview = Stateful_modules.Variable (struct
 
432
  type t = treeview
 
433
  let name = Some "treeview_ifconfig"
 
434
  end)
 
435
let extract = The_unique_treeview.extract
 
436
 
 
437
let make ~(window:GWindow.window) ~(hbox:GPack.box) ~after_user_edit_callback () =
 
438
  let result = new t ~packing:(hbox#add) ~after_user_edit_callback () in
 
439
  let () = Treeview.add_expand_and_collapse_button ~window ~hbox (result:>Treeview.t) in
 
440
  The_unique_treeview.set result;
 
441
  result
 
442
;;