Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions ocaml/idl/datamodel.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3831,6 +3831,48 @@ module VIF = struct
]
~allowed_roles:_R_VM_OP ()

let add_trunks =
call ~name:"add_trunks" ~lifecycle:[]
~doc:"Associates a 802.1Q VLAN with this VIF"
~params:
[
( Ref _vif
, "self"
, "The VIF which the 802.1Q VLAN will be associated with"
)
; (Int, "value", "The 802.1Q VLAN which will be associated with the VIF")
]
~allowed_roles:_R_VM_ADMIN ()

let remove_trunks =
call ~name:"remove_trunks" ~lifecycle:[]
~doc:"Removes a 802.1Q VLAN from this VIF"
~params:
[
( Ref _vif
, "self"
, "The VIF from which the 802.1Q VLAN will be removed"
)
; (Int, "value", "The 802.1Q VLAN which will be removed from the VIF")
]
~allowed_roles:_R_VM_ADMIN ()

let set_trunks =
call ~name:"set_trunks" ~lifecycle:[]
~doc:"Set the 802.1Q VLANs to which traffic on this VIF can be restricted"
~params:
[
( Ref _vif
, "self"
, "The VIF which the 802.1Q VLANs will be associated with"
)
; ( Set Int
, "value"
, "The 802.1Q VLANs which will be associated with the VIF"
)
]
~allowed_roles:_R_VM_ADMIN ()

(** A virtual network interface *)
let t =
create_obj ~in_db:true
Expand All @@ -3854,6 +3896,9 @@ module VIF = struct
; remove_ipv6_allowed
; configure_ipv4
; configure_ipv6
; add_trunks
; remove_trunks
; set_trunks
]
~contents:
([
Expand Down Expand Up @@ -4044,6 +4089,10 @@ module VIF = struct
~internal_only:true ~qualifier:DynamicRO "reserved_pci"
"pci of network SR-IOV VF which is reserved for this vif"
~default_value:(Some (VRef null_ref))
; field ~qualifier:StaticRO ~lifecycle:[] ~ty:(Set Int)
~default_value:(Some (VSet [])) "trunks"
"the 802.1Q VLANs that this port trunks (if available) ; if it \
is empty, then the port trunks all VLANs."
]
)
()
Expand Down
2 changes: 1 addition & 1 deletion ocaml/idl/datamodel_common.ml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ open Datamodel_roles
to leave a gap for potential hotfixes needing to increment the schema version.*)
let schema_major_vsn = 5

let schema_minor_vsn = 906
let schema_minor_vsn = 907

(* Historical schema versions just in case this is useful later *)
let rio_schema_major_vsn = 5
Expand Down
6 changes: 4 additions & 2 deletions ocaml/idl/datamodel_errors.ml
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ let _ =
~doc:"The network is incompatible with bond" () ;
error Api_errors.network_incompatible_with_tunnel ["network"]
~doc:"The network is incompatible with tunnel" () ;
error Api_errors.network_incompatible_with_trunks ["network"]
~doc:"The network is incompatible with VIF using trunks." () ;
error Api_errors.pool_joining_host_has_network_sriovs []
~doc:"The host joining the pool must not have any network SR-IOVs." () ;

Expand Down Expand Up @@ -392,8 +394,8 @@ let _ =

error Api_errors.vlan_tag_invalid ["VLAN"]
~doc:
"You tried to create a VLAN, but the tag you gave was invalid -- it must \
be between 0 and 4094. The parameter echoes the VLAN tag you gave."
"The VLAN tag you gave was invalid -- it must be between 0 and 4094. The \
parameter echoes the VLAN tag you gave."
() ;
error Api_errors.network_contains_vif ["vifs"]
~doc:"The network contains active VIFs and cannot be deleted." () ;
Expand Down
8 changes: 8 additions & 0 deletions ocaml/idl/datamodel_lifecycle.ml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ let prototyped_of_field = function
Some "26.15.0"
| "SM", "host_pending_features" ->
Some "24.37.0"
| "VIF", "trunks" ->
Some "26.15.0-next"
| "host", "timezone" ->
Some "26.0.0"
| "host", "ntp_custom_servers" ->
Expand Down Expand Up @@ -253,6 +255,12 @@ let prototyped_of_message = function
Some "22.26.0"
| "VDI", "revert" ->
Some "26.15.0-next"
| "VIF", "set_trunks" ->
Some "26.15.0-next"
| "VIF", "remove_trunks" ->
Some "26.15.0-next"
| "VIF", "add_trunks" ->
Some "26.15.0-next"
| "host", "set_servertime" ->
Some "26.0.0"
| "host", "get_ntp_synchronized" ->
Expand Down
2 changes: 1 addition & 1 deletion ocaml/idl/schematest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ let hash x = Digest.string x |> Digest.to_hex
(* BEWARE: if this changes, check that schema has been bumped accordingly in
ocaml/idl/datamodel_common.ml, usually schema_minor_vsn *)

let last_known_schema_hash = "6147ef4f0f9c3bbbf0c2061e0a0d0010"
let last_known_schema_hash = "e0cc6f552213dc9f47f367b9db97c218"

let current_schema_hash : string =
let open Datamodel_types in
Expand Down
4 changes: 2 additions & 2 deletions ocaml/tests/common/test_common.ml
Original file line number Diff line number Diff line change
Expand Up @@ -278,15 +278,15 @@ let make_vif ~__context ?(ref = Ref.make ()) ?(uuid = make_uuid ())
?(ipv4_allowed = []) ?(ipv6_allowed = []) ?(ipv4_configuration_mode = `None)
?(ipv4_addresses = []) ?(ipv4_gateway = "")
?(ipv6_configuration_mode = `None) ?(ipv6_addresses = [])
?(ipv6_gateway = "") () =
?(ipv6_gateway = "") ?(trunks = []) () =
Db.VIF.create ~__context ~ref ~uuid ~current_operations ~allowed_operations
~reserved ~device ~network ~vM ~mAC ~mAC_autogenerated ~mTU
~qos_algorithm_type ~qos_algorithm_params ~qos_supported_algorithms
~currently_attached ~status_code ~status_detail ~runtime_properties
~other_config ~metrics ~locking_mode ~ipv4_allowed ~ipv6_allowed
~ipv4_configuration_mode ~ipv4_addresses ~ipv4_gateway
~ipv6_configuration_mode ~ipv6_addresses ~ipv6_gateway
~reserved_pci:Ref.null ;
~reserved_pci:Ref.null ~trunks ;
ref

let make_pool ~__context ~master ?(name_label = "") ?(name_description = "")
Expand Down
1 change: 1 addition & 0 deletions ocaml/tests/suite_alcotest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ let () =
; ("Test_pvs_proxy", Test_pvs_proxy.test)
; ("Test_pvs_server", Test_pvs_server.test)
; ("Test_vif_helpers", Test_vif_helpers.test)
; ("Test_vif_trunks", Test_vif_trunks.test)
; ("Test_vm_memory_constraints", Test_vm_memory_constraints.test)
; ("Test_xapi_xenops", Test_xapi_xenops.test)
; ("Test_network_event_loop", Test_network_event_loop.test)
Expand Down
4 changes: 2 additions & 2 deletions ocaml/tests/test_vif_helpers.ml
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ let create ~__context ~device ~network ~vM ?(mAC = "00:00:00:00:00:00")
?(locking_mode = `unlocked) ?(ipv4_allowed = []) ?(ipv6_allowed = [])
?(ipv4_configuration_mode = `None) ?(ipv4_addresses = [])
?(ipv4_gateway = "") ?(ipv6_configuration_mode = `None)
?(ipv6_addresses = []) ?(ipv6_gateway = "") () =
?(ipv6_addresses = []) ?(ipv6_gateway = "") ?(trunks = []) () =
Xapi_vif_helpers.create ~__context ~device ~network ~vM ~mAC ~mTU
~other_config ~qos_algorithm_type ~qos_algorithm_params ~currently_attached
~locking_mode ~ipv4_allowed ~ipv6_allowed ~ipv4_configuration_mode
~ipv4_addresses ~ipv4_gateway ~ipv6_configuration_mode ~ipv6_addresses
~ipv6_gateway
~ipv6_gateway ~trunks

let test_create_ok () =
let __context = T.make_test_database () in
Expand Down
180 changes: 180 additions & 0 deletions ocaml/tests/test_vif_trunks.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
(*
* Copyright (C) 2026 Vates
* Copyright (C) Citrix Systems Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; version 2.1 only. with the special
* exception on linking described in file LICENSE.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*)

module T = Test_common

let test_trunks_parameter () =
let __context = T.make_test_database () in
let vM = T.make_vm ~__context () in
let network = T.make_network ~__context () in
let vif = T.make_vif ~__context ~device:"1" ~vM ~network ~trunks:[2201L] () in
Alcotest.(check (list int64))
"test_trunks_parameter testing add_trunks" [3201L; 2201L]
( Xapi_vif.add_trunks ~__context ~self:vif ~value:3201L ;
Db.VIF.get_trunks ~__context ~self:vif
) ;
Alcotest.(check (list int64))
"test_trunks_parameter testing remove_trunks" [2201L]
( Xapi_vif.remove_trunks ~__context ~self:vif ~value:3201L ;
Db.VIF.get_trunks ~__context ~self:vif
) ;
Alcotest.(check (list int64))
"test_trunks_parameter testing set_trunks" [3201L]
( Xapi_vif.set_trunks ~__context ~self:vif ~value:[3201L] ;
Db.VIF.get_trunks ~__context ~self:vif
) ;
Alcotest.(check (list int64))
"test_trunks_parameter testing set_trunks empty" []
( Xapi_vif.set_trunks ~__context ~self:vif ~value:[] ;
Db.VIF.get_trunks ~__context ~self:vif
) ;
Alcotest.(check_raises)
"test_trunks_parameter testing invalid VLAN tag"
Api_errors.(Server_error (Api_errors.vlan_tag_invalid, ["9999"]))
(fun () -> Xapi_vif.add_trunks ~__context ~self:vif ~value:9999L)

(** try to set trunks on VIF on incompatible network *)
let test_trunks_coherence_vif_set () =
let __context = T.make_test_database () in
(* create a VLAN *)
let host = T.make_host ~__context () in
let network = T.make_network ~__context () in
let tagged_PIF = T.make_pif ~__context ~network ~host () in
let tag = 3201L in
let vlan_network = T.make_network ~__context ~bridge:"xapi0" () in
let untagged_PIF =
T.make_pif ~__context ~network:vlan_network ~host ~vLAN:tag ()
in
let _vlan = T.make_vlan ~__context ~tagged_PIF ~untagged_PIF ~tag () in
(* create VM + VIF using this network *)
let vM = T.make_vm ~__context () in
let vif = T.make_vif ~__context ~device:"1" ~vM ~network:vlan_network () in
Alcotest.(check_raises)
"test_trunks_coherence_vif_set testing"
Api_errors.(
Server_error
( Api_errors.network_incompatible_with_trunks
, [Ref.string_of vlan_network]
)
)
(fun () -> Xapi_vif.add_trunks ~__context ~self:vif ~value:2201L)

(** try to add VIF (with trunks) on incompatible network *)
let test_trunks_coherence_vif_add () =
let __context = T.make_test_database () in
(* create a VLAN *)
let host = T.make_host ~__context () in
let network = T.make_network ~__context () in
let tagged_PIF = T.make_pif ~__context ~network ~host () in
let tag = 3201L in
let vlan_network = T.make_network ~__context ~bridge:"xapi0" () in
let untagged_PIF =
T.make_pif ~__context ~network:vlan_network ~host ~vLAN:tag ()
in
let _vlan = T.make_vlan ~__context ~tagged_PIF ~untagged_PIF ~tag () in
(* create VM + VIF using this network *)
let vM = T.make_vm ~__context () in
Alcotest.(check_raises)
"test_trunks_coherence_vif_add testing"
Api_errors.(
Server_error
( Api_errors.network_incompatible_with_trunks
, [Ref.string_of vlan_network]
)
)
(fun () ->
let _ : API.ref_VIF =
Xapi_vif.create ~__context ~device:"1" ~network:vlan_network ~vM
~mAC:"00:00:00:00:00:00" ~mTU:1500L ~other_config:[]
~currently_attached:true ~qos_algorithm_type:""
~qos_algorithm_params:[] ~locking_mode:`unlocked ~ipv4_allowed:[]
~ipv6_allowed:[] ~trunks:[2201L]
in
()
)

(** try to associate PIF (with VLAN) on Network with trunked-VIF *)
let test_trunks_coherence_pif_vlan () =
let __context = T.make_test_database () in
(* create VM + VIF on plain network *)
let network = T.make_network ~__context () in
let vM = T.make_vm ~__context () in
let _vif =
T.make_vif ~__context ~device:"1" ~vM ~network ~trunks:[3201L] ()
in
Alcotest.(check_raises)
"test_trunks_coherence_pif_vlan testing"
Api_errors.(
Server_error
(Api_errors.network_incompatible_with_trunks, [Ref.string_of network])
)
(fun () ->
(* create a VLAN *)
let host = T.make_host ~__context () in
let network2 = T.make_network ~__context () in
let tagged_PIF = T.make_pif ~__context ~network:network2 ~host () in
let tag = 2201L in
let untagged_PIF = T.make_pif ~__context ~network ~host ~vLAN:tag () in
let _vlan = T.make_vlan ~__context ~tagged_PIF ~untagged_PIF ~tag () in
()
)

let test_trunks_move () =
let __context = T.make_test_database () in
(* create VM + VIF using this network *)
let network1 = T.make_network ~__context () in
let vM = T.make_vm ~__context () in
let vif = T.make_vif ~__context ~network:network1 ~vM () in
let network2 = T.make_network ~__context () in
Alcotest.(check unit)
"test_trunks_move testing" ()
(Xapi_vif.move ~__context ~self:vif ~network:network2)

let test_trunks_move_to_vlan () =
let __context = T.make_test_database () in
(* create VM + VIF (with trunks) *)
let network1 = T.make_network ~__context () in
let vM = T.make_vm ~__context () in
let vif = T.make_vif ~__context ~network:network1 ~vM ~trunks:[3201L] () in
(* create a VLAN *)
let host = T.make_host ~__context () in
let network2 = T.make_network ~__context () in
let tagged_PIF = T.make_pif ~__context ~network:network2 ~host () in
let tag = 3201L in
let vlan_network2 = T.make_network ~__context ~bridge:"xapi0" () in
let untagged_PIF =
T.make_pif ~__context ~network:vlan_network2 ~host ~vLAN:tag ()
in
let _vlan = T.make_vlan ~__context ~tagged_PIF ~untagged_PIF ~tag () in
(* move the VIF to the vlan_network *)
Alcotest.(check_raises)
"test_trunks_move_to_vlan testing"
Api_errors.(
Server_error
( Api_errors.network_incompatible_with_trunks
, [Ref.string_of vlan_network2]
)
)
(fun () -> Xapi_vif.move ~__context ~self:vif ~network:vlan_network2)

let test =
[
("test_trunks_parameter", `Quick, test_trunks_parameter)
; ("test_trunks_coherence_vif_set", `Quick, test_trunks_coherence_vif_set)
; ("test_trunks_coherence_vif_add", `Quick, test_trunks_coherence_vif_add)
; ("test_trunks_coherence_pif_vlan", `Quick, test_trunks_coherence_pif_vlan)
; ("test_trunks_move", `Quick, test_trunks_move)
; ("test_trunks_move_to_vlan", `Quick, test_trunks_move_to_vlan)
]
2 changes: 1 addition & 1 deletion ocaml/xapi-cli-server/cli_operations.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2859,7 +2859,7 @@ let vif_create printer rpc session_id params =
Client.VIF.create ~rpc ~session_id ~device ~network ~vM ~mAC ~mTU
~other_config:[] ~currently_attached:false ~qos_algorithm_type:""
~qos_algorithm_params:[] ~locking_mode:`network_default ~ipv4_allowed:[]
~ipv6_allowed:[]
~ipv6_allowed:[] ~trunks:[]
in
let uuid = Client.VIF.get_uuid ~rpc ~session_id ~self:vif in
printer (Cli_printer.PList [uuid])
Expand Down
17 changes: 17 additions & 0 deletions ocaml/xapi-cli-server/records.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,23 @@ let vif_record rpc session_id vif =
; make_field ~name:"ipv6-gateway"
~get:(fun () -> (x ()).API.vIF_ipv6_gateway)
()
; make_field ~name:"trunks"
~get:(fun () -> map_and_concat Int64.to_string (x ()).API.vIF_trunks)
~get_set:(fun () -> List.map Int64.to_string (x ()).API.vIF_trunks)
~add_to_set:(fun value ->
let value = safe_i64_of_string "value" value in
Client.VIF.add_trunks ~rpc ~session_id ~self:vif ~value
)
~remove_from_set:(fun value ->
let value = safe_i64_of_string "value" value in
Client.VIF.remove_trunks ~rpc ~session_id ~self:vif ~value
)
~set:(fun value ->
Client.VIF.set_trunks ~rpc ~session_id ~self:vif
~value:
(List.map (safe_i64_of_string "value") (get_words ',' value))
)
()
]
}

Expand Down
3 changes: 3 additions & 0 deletions ocaml/xapi-consts/api_errors.ml
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ let network_has_incompatible_sriov_pifs =
let network_has_incompatible_vlan_on_sriov_pifs =
add_error "NETWORK_HAS_INCOMPATIBLE_VLAN_ON_SRIOV_PIFS"

let network_incompatible_with_trunks =
add_error "NETWORK_INCOMPATIBLE_WITH_TRUNKS"

let operation_not_allowed = add_error "OPERATION_NOT_ALLOWED"

let operation_blocked = add_error "OPERATION_BLOCKED"
Expand Down
Loading
Loading