55 "fmt"
66 "log"
77 "os"
8+ "strconv"
9+ "strings"
810 "text/tabwriter"
911
1012 v1 "cloudstackctl/apis/v1"
@@ -49,7 +51,7 @@ func ListNetworks(name string) error {
4951 return fmt .Errorf ("cloudstack API error: %w" , err )
5052 }
5153 w := tabwriter .NewWriter (os .Stdout , 0 , 0 , 2 , ' ' , 0 )
52- fmt .Fprintln (w , "NAME\t ID\t ZONE\t DISPLAY TEXT\t TYPE\t STATE" )
54+ fmt .Fprintln (w , "NAME\t ID\t ZONE\t VLAN \ t DISPLAY TEXT\t TYPE\t STATE" )
5355 for _ , n := range resp .Networks {
5456 display := n .Displaytext
5557 if display == "" {
@@ -66,7 +68,21 @@ func ListNetworks(name string) error {
6668 }
6769 }
6870
69- fmt .Fprintf (w , "%s\t %s\t %s\t %s\t %s\t %s\n " , n .Name , n .Id , zoneName , display , n .Type , n .State )
71+ // Attempt to extract VLAN information from the returned network object
72+ // via JSON to avoid SDK field name differences across versions.
73+ vlan := ""
74+ if b , merr := json .Marshal (n ); merr == nil {
75+ var m map [string ]interface {}
76+ if uerr := json .Unmarshal (b , & m ); uerr == nil {
77+ if v , ok := m ["vlan" ].(string ); ok {
78+ vlan = v
79+ } else if v , ok := m ["vlanid" ].(string ); ok {
80+ vlan = v
81+ }
82+ }
83+ }
84+
85+ fmt .Fprintf (w , "%s\t %s\t %s\t %s\t %s\t %s\t %s\n " , n .Name , n .Id , zoneName , vlan , display , n .Type , n .State )
7086 }
7187 w .Flush ()
7288 return nil
@@ -150,10 +166,60 @@ func ApplyNetwork(netRes *v1.Network) error {
150166 if zerr != nil {
151167 return fmt .Errorf ("failed to resolve zone %s: %w" , netRes .Spec .Zone , zerr )
152168 }
153- createParams := client .Network .NewCreateNetworkParams (name , netRes .Spec .NetworkOffering , zoneID )
169+ // Resolve network offering name to ID; require resolution.
170+ offeringID , offErr := ResolveNetworkOffering (netRes .Spec .NetworkOffering )
171+ if offErr != nil {
172+ return fmt .Errorf ("failed to resolve network offering %s: %w" , netRes .Spec .NetworkOffering , offErr )
173+ }
174+ createParams := client .Network .NewCreateNetworkParams (name , offeringID , zoneID )
154175 if netRes .Spec .Description != "" {
155176 createParams .SetDisplaytext (netRes .Spec .Description )
156177 }
178+
179+ // Pass bypassvlanoverlapcheck through to CloudStack (supported by API)
180+ createParams .SetBypassvlanoverlapcheck (netRes .Spec .BypassVlanOverlapCheck )
181+
182+ // If shared network fields are present, set them on the create params.
183+ if netRes .Spec .Gateway != "" {
184+ createParams .SetGateway (netRes .Spec .Gateway )
185+ }
186+ if netRes .Spec .Netmask != "" {
187+ createParams .SetNetmask (netRes .Spec .Netmask )
188+ }
189+ if netRes .Spec .StartIP != "" {
190+ createParams .SetStartip (netRes .Spec .StartIP )
191+ }
192+ if netRes .Spec .EndIP != "" {
193+ createParams .SetEndip (netRes .Spec .EndIP )
194+ }
195+ if netRes .Spec .Vlan != nil {
196+ var vlanVal string
197+ switch v := netRes .Spec .Vlan .(type ) {
198+ case string :
199+ vlanVal = v
200+ case int :
201+ vlanVal = strconv .Itoa (v )
202+ case int64 :
203+ vlanVal = strconv .FormatInt (v , 10 )
204+ case float64 :
205+ // YAML numbers may be decoded as float64
206+ vlanVal = strconv .FormatInt (int64 (v ), 10 )
207+ default :
208+ vlanVal = fmt .Sprintf ("%v" , v )
209+ }
210+ // Accept numeric VLANs like "1000" or the full URI "vlan://1000".
211+ // If the value already contains a scheme ("://"), trust it
212+ // (supports vlan://, vxlan://, etc.). Only numeric values
213+ // without a scheme get prefixed with "vlan://".
214+ if vlanVal != "" {
215+ if ! strings .Contains (vlanVal , "://" ) {
216+ if _ , err := strconv .Atoi (vlanVal ); err == nil {
217+ vlanVal = "vlan://" + vlanVal
218+ }
219+ }
220+ createParams .SetVlan (vlanVal )
221+ }
222+ }
157223 resp , err := client .Network .CreateNetwork (createParams )
158224 if err != nil {
159225 return fmt .Errorf ("cloudstack create network error: %w" , err )
0 commit comments