github.com/vmware/go-vcloud-director/v2@v2.24.0/govcd/vapp_network.go (about) 1 /* 2 * Copyright 2020 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. 3 */ 4 5 package govcd 6 7 import ( 8 "fmt" 9 "github.com/vmware/go-vcloud-director/v2/types/v56" 10 "github.com/vmware/go-vcloud-director/v2/util" 11 "net/http" 12 "strings" 13 ) 14 15 // UpdateNetworkFirewallRules updates vApp networks firewall rules. It will overwrite existing ones as there is 16 // no 100% way to identify them separately. 17 // Returns pointer to types.VAppNetwork or error 18 func (vapp *VApp) UpdateNetworkFirewallRules(networkId string, firewallRules []*types.FirewallRule, enabled bool, defaultAction string, logDefaultAction bool) (*types.VAppNetwork, error) { 19 task, err := vapp.UpdateNetworkFirewallRulesAsync(networkId, firewallRules, enabled, defaultAction, logDefaultAction) 20 if err != nil { 21 return nil, err 22 } 23 err = task.WaitTaskCompletion() 24 if err != nil { 25 return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) 26 } 27 28 return vapp.GetVappNetworkById(networkId, false) 29 } 30 31 // UpdateNetworkFirewallRulesAsync asynchronously updates vApp networks firewall rules. It will overwrite existing ones 32 // as there is no 100% way to identify them separately. 33 // Returns task or error 34 func (vapp *VApp) UpdateNetworkFirewallRulesAsync(networkId string, firewallRules []*types.FirewallRule, enabled bool, defaultAction string, logDefaultAction bool) (Task, error) { 35 util.Logger.Printf("[TRACE] UpdateNetworkFirewallRulesAsync with values: id: %s and firewallServiceConfiguration: %#v", networkId, firewallRules) 36 uuid := extractUuid(networkId) 37 networkToUpdate, err := vapp.GetVappNetworkById(uuid, true) 38 if err != nil { 39 return Task{}, err 40 } 41 42 if networkToUpdate.Configuration.Features == nil { 43 networkToUpdate.Configuration.Features = &types.NetworkFeatures{} 44 } 45 networkToUpdate.Xmlns = types.XMLNamespaceVCloud 46 47 // If API didn't return Firewall service XML part, that means vApp network isn't connected to org network or not fenced. 48 // In other words there isn't firewall when you connected directly or isolated. 49 if networkToUpdate.Configuration.Features.FirewallService == nil { 50 return Task{}, fmt.Errorf("provided network isn't connecd to org network or isn't fenced") 51 } 52 networkToUpdate.Configuration.Features.FirewallService.IsEnabled = enabled 53 networkToUpdate.Configuration.Features.FirewallService.LogDefaultAction = logDefaultAction 54 networkToUpdate.Configuration.Features.FirewallService.DefaultAction = defaultAction 55 networkToUpdate.Configuration.Features.FirewallService.FirewallRule = firewallRules 56 57 // here we use `PUT /network/{id}` which allow to change vApp network. 58 // But `GET /network/{id}` can return org VDC network or vApp network. 59 apiEndpoint := vapp.client.VCDHREF 60 apiEndpoint.Path += "/network/" + uuid 61 62 return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut, 63 types.MimeVappNetwork, "error updating vApp Network firewall rules: %s", networkToUpdate) 64 } 65 66 // GetVappNetworkById returns a VApp network reference if the vApp network ID matches an existing one. 67 // If no valid VApp network is found, it returns a nil VApp network reference and an error 68 func (vapp *VApp) GetVappNetworkById(id string, refresh bool) (*types.VAppNetwork, error) { 69 util.Logger.Printf("[TRACE] [GetVappNetworkById] getting vApp Network: %s and refresh %t", id, refresh) 70 71 if refresh { 72 err := vapp.Refresh() 73 if err != nil { 74 return nil, fmt.Errorf("error refreshing vApp: %s", err) 75 } 76 } 77 78 //vApp Might Not Have Any networks 79 if vapp.VApp.NetworkConfigSection == nil || len(vapp.VApp.NetworkConfigSection.NetworkConfig) == 0 { 80 return nil, ErrorEntityNotFound 81 } 82 83 util.Logger.Printf("[TRACE] Looking for networks: %s --- %d", id, len(vapp.VApp.NetworkConfigSection.NetworkConfig)) 84 for _, vappNetwork := range vapp.VApp.NetworkConfigSection.NetworkConfig { 85 // Break early for empty network interfaces. They don't have all information 86 if vappNetwork.NetworkName == types.NoneNetwork { 87 continue 88 } 89 util.Logger.Printf("[TRACE] Looking at: %s", vappNetwork.Link.HREF) 90 if equalIds(id, vappNetwork.ID, vappNetwork.Link.HREF) { 91 vappNetwork := &types.VAppNetwork{} 92 93 apiEndpoint := vapp.client.VCDHREF 94 apiEndpoint.Path += "/network/" + extractUuid(id) 95 96 _, err := vapp.client.ExecuteRequest(apiEndpoint.String(), http.MethodGet, 97 types.MimeVappNetwork, "error getting vApp network: %s", nil, vappNetwork) 98 if err != nil { 99 return nil, err 100 } 101 return vappNetwork, nil 102 } 103 } 104 util.Logger.Printf("[TRACE] GetVappNetworkById returns not found entity") 105 return nil, ErrorEntityNotFound 106 } 107 108 // GetVappNetworkByName returns a VAppNetwork reference if the vApp network name matches an existing one. 109 // If no valid vApp network is found, it returns a nil VAppNetwork reference and an error 110 func (vapp *VApp) GetVappNetworkByName(vappNetworkName string, refresh bool) (*types.VAppNetwork, error) { 111 util.Logger.Printf("[TRACE] [GetVappNetworkByName] getting vApp Network: %s and refresh %t", vappNetworkName, refresh) 112 if refresh { 113 err := vapp.Refresh() 114 if err != nil { 115 return nil, fmt.Errorf("error refreshing vApp: %s", err) 116 } 117 } 118 119 //vApp Might Not Have Any networks 120 if vapp.VApp.NetworkConfigSection == nil || len(vapp.VApp.NetworkConfigSection.NetworkConfig) == 0 { 121 return nil, ErrorEntityNotFound 122 } 123 124 util.Logger.Printf("[TRACE] Looking for networks: %s", vappNetworkName) 125 for _, vappNetwork := range vapp.VApp.NetworkConfigSection.NetworkConfig { 126 127 util.Logger.Printf("[TRACE] Looking at: %s", vappNetwork.NetworkName) 128 if vappNetwork.NetworkName == vappNetworkName { 129 return vapp.GetVappNetworkById(extractUuid(vappNetwork.Link.HREF), refresh) 130 } 131 132 } 133 util.Logger.Printf("[TRACE] Couldn't find vApp network: %s", vappNetworkName) 134 return nil, ErrorEntityNotFound 135 } 136 137 // GetVappNetworkByNameOrId returns a types.VAppNetwork reference if either the vApp network name or ID matches an existing one. 138 // If no valid vApp network is found, it returns a nil types.VAppNetwork reference and an error 139 func (vapp *VApp) GetVappNetworkByNameOrId(identifier string, refresh bool) (*types.VAppNetwork, error) { 140 getByName := func(name string, refresh bool) (interface{}, error) { return vapp.GetVappNetworkByName(name, refresh) } 141 getById := func(id string, refresh bool) (interface{}, error) { return vapp.GetVappNetworkById(id, refresh) } 142 entity, err := getEntityByNameOrId(getByName, getById, identifier, false) 143 if entity == nil { 144 return nil, err 145 } 146 return entity.(*types.VAppNetwork), err 147 } 148 149 // UpdateNetworkNatRules updates vApp networks NAT rules. 150 // Returns pointer to types.VAppNetwork or error 151 func (vapp *VApp) UpdateNetworkNatRules(networkId string, natRules []*types.NatRule, enabled bool, natType, policy string) (*types.VAppNetwork, error) { 152 task, err := vapp.UpdateNetworkNatRulesAsync(networkId, natRules, enabled, natType, policy) 153 if err != nil { 154 return nil, err 155 } 156 err = task.WaitTaskCompletion() 157 if err != nil { 158 return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) 159 } 160 161 return vapp.GetVappNetworkById(networkId, false) 162 } 163 164 // UpdateNetworkNatRulesAsync asynchronously updates vApp NAT rules. 165 // Returns task or error 166 func (vapp *VApp) UpdateNetworkNatRulesAsync(networkId string, natRules []*types.NatRule, enabled bool, natType, policy string) (Task, error) { 167 util.Logger.Printf("[TRACE] UpdateNetworkNatRulesAsync with values: id: %s and natRules: %#v", networkId, natRules) 168 169 uuid := extractUuid(networkId) 170 networkToUpdate, err := vapp.GetVappNetworkById(uuid, true) 171 if err != nil { 172 return Task{}, err 173 } 174 175 if networkToUpdate.Configuration.Features == nil { 176 networkToUpdate.Configuration.Features = &types.NetworkFeatures{} 177 } 178 networkToUpdate.Xmlns = types.XMLNamespaceVCloud 179 180 // if services are empty return by API, then we can deduce that network isn't connected to Org network or fenced 181 if networkToUpdate.Configuration.Features.NatService == nil && networkToUpdate.Configuration.Features.FirewallService == nil { 182 return Task{}, fmt.Errorf("provided network isn't connected to org network or isn't fenced") 183 } 184 if networkToUpdate.Configuration.Features.NatService == nil { 185 networkToUpdate.Configuration.Features.NatService = &types.NatService{} 186 } 187 networkToUpdate.Configuration.Features.NatService.IsEnabled = enabled 188 networkToUpdate.Configuration.Features.NatService.NatType = natType 189 networkToUpdate.Configuration.Features.NatService.Policy = policy 190 networkToUpdate.Configuration.Features.NatService.NatRule = natRules 191 192 // here we use `PUT /network/{id}` which allow to change vApp network. 193 // But `GET /network/{id}` can return org VDC network or vApp network. 194 apiEndpoint := vapp.client.VCDHREF 195 apiEndpoint.Path += "/network/" + uuid 196 197 return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut, 198 types.MimeVappNetwork, "error updating vApp Network NAT rules: %s", networkToUpdate) 199 } 200 201 // RemoveAllNetworkNatRules removes all NAT rules from a vApp network 202 // Returns error 203 func (vapp *VApp) RemoveAllNetworkNatRules(networkId string) error { 204 task, err := vapp.UpdateNetworkNatRulesAsync(networkId, []*types.NatRule{}, false, "ipTranslation", "allowTraffic") 205 if err != nil { 206 return err 207 } 208 err = task.WaitTaskCompletion() 209 if err != nil { 210 return fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) 211 } 212 return nil 213 } 214 215 // RemoveAllNetworkFirewallRules removes all network firewall rules from a vApp network. 216 // Returns error 217 func (vapp *VApp) RemoveAllNetworkFirewallRules(networkId string) error { 218 networkToUpdate, err := vapp.GetVappNetworkById(networkId, true) 219 if err != nil { 220 return err 221 } 222 task, err := vapp.UpdateNetworkFirewallRulesAsync(networkId, []*types.FirewallRule{}, false, 223 networkToUpdate.Configuration.Features.FirewallService.DefaultAction, networkToUpdate.Configuration.Features.FirewallService.LogDefaultAction) 224 if err != nil { 225 return err 226 } 227 err = task.WaitTaskCompletion() 228 if err != nil { 229 return fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) 230 } 231 return nil 232 } 233 234 // UpdateNetworkStaticRouting updates vApp network static routes. 235 // Returns pointer to types.VAppNetwork or error 236 func (vapp *VApp) UpdateNetworkStaticRouting(networkId string, staticRoutes []*types.StaticRoute, enabled bool) (*types.VAppNetwork, error) { 237 task, err := vapp.UpdateNetworkStaticRoutingAsync(networkId, staticRoutes, enabled) 238 if err != nil { 239 return nil, err 240 } 241 err = task.WaitTaskCompletion() 242 if err != nil { 243 return nil, fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) 244 } 245 246 return vapp.GetVappNetworkById(networkId, false) 247 } 248 249 // UpdateNetworkStaticRoutingAsync asynchronously updates vApp network static routes. 250 // Returns task or error 251 func (vapp *VApp) UpdateNetworkStaticRoutingAsync(networkId string, staticRoutes []*types.StaticRoute, enabled bool) (Task, error) { 252 util.Logger.Printf("[TRACE] UpdateNetworkStaticRoutingAsync with values: id: %s and staticRoutes: %#v, enable: %t", networkId, staticRoutes, enabled) 253 254 uuid := extractUuid(networkId) 255 networkToUpdate, err := vapp.GetVappNetworkById(uuid, true) 256 if err != nil { 257 return Task{}, err 258 } 259 260 if !IsVappNetwork(networkToUpdate.Configuration) { 261 return Task{}, fmt.Errorf("network static routing can be applied only for vapp network, not vapp org network") 262 } 263 264 if networkToUpdate.Configuration.Features == nil { 265 networkToUpdate.Configuration.Features = &types.NetworkFeatures{} 266 } 267 networkToUpdate.Xmlns = types.XMLNamespaceVCloud 268 269 networkToUpdate.Configuration.Features.StaticRoutingService = &types.StaticRoutingService{IsEnabled: enabled, StaticRoute: staticRoutes} 270 271 // here we use `PUT /network/{id}` which allow to change vApp network. 272 // But `GET /network/{id}` can return org VDC network or vApp network. 273 apiEndpoint := vapp.client.VCDHREF 274 apiEndpoint.Path += "/network/" + uuid 275 276 return vapp.client.ExecuteTaskRequest(apiEndpoint.String(), http.MethodPut, 277 types.MimeVappNetwork, "error updating vApp Network static routes: %s", networkToUpdate) 278 } 279 280 // IsVappNetwork allows to identify if given network config is a vApp network and not a vApp Org network 281 func IsVappNetwork(networkConfig *types.NetworkConfiguration) bool { 282 if networkConfig.FenceMode == types.FenceModeIsolated || 283 (networkConfig.FenceMode == types.FenceModeNAT && networkConfig.IPScopes != nil && 284 networkConfig.IPScopes.IPScope != nil && len(networkConfig.IPScopes.IPScope) > 0 && 285 !networkConfig.IPScopes.IPScope[0].IsInherited) { 286 return true 287 } 288 return false 289 } 290 291 // RemoveAllNetworkStaticRoutes removes all static routes from a vApp network 292 // Returns error 293 func (vapp *VApp) RemoveAllNetworkStaticRoutes(networkId string) error { 294 task, err := vapp.UpdateNetworkStaticRoutingAsync(networkId, []*types.StaticRoute{}, false) 295 if err != nil { 296 return err 297 } 298 err = task.WaitTaskCompletion() 299 if err != nil { 300 return fmt.Errorf("%s", combinedTaskErrorMessage(task.Task, err)) 301 } 302 return nil 303 } 304 305 // queryVappNetworks returns a list of vApp networks with an optional filter 306 func queryVappNetworks(client *Client, values map[string]string) ([]*types.QueryResultVappNetworkRecordType, error) { 307 308 vAppNetworkType := types.QtVappNetwork 309 if client.IsSysAdmin { 310 vAppNetworkType = types.QtAdminVappNetwork 311 } 312 313 params := map[string]string{ 314 "type": vAppNetworkType, 315 } 316 filterValue := "" 317 if len(values) > 0 { 318 var filterElements []string 319 for k, v := range values { 320 item := fmt.Sprintf("%s==%s", k, v) 321 filterElements = append(filterElements, item) 322 } 323 filterValue = strings.Join(filterElements, ";") 324 } 325 if filterValue != "" { 326 params["filter"] = filterValue 327 } 328 results, err := client.cumulativeQuery(vAppNetworkType, nil, params) 329 if err != nil { 330 return nil, fmt.Errorf("error retrieving vApp networks %s", err) 331 } 332 333 if client.IsSysAdmin { 334 return results.Results.AdminVappNetworkRecord, nil 335 } 336 return results.Results.VappNetworkRecord, nil 337 } 338 339 // QueryVappNetworks returns all vApp networks visible to the client 340 func (client *Client) QueryVappNetworks(values map[string]string) ([]*types.QueryResultVappNetworkRecordType, error) { 341 return queryVappNetworks(client, values) 342 } 343 344 // QueryAllVappNetworks returns all vApp networks and vApp Org Networks belonging to the current vApp 345 func (vapp *VApp) QueryAllVappNetworks(values map[string]string) ([]*types.QueryResultVappNetworkRecordType, error) { 346 // Note: when querying a field that contains a UUID, the system compares only the UUIDs, even if the full field contains more than that. 347 allValues := map[string]string{"vApp": extractUuid(vapp.VApp.ID)} 348 for k, v := range values { 349 allValues[k] = v 350 } 351 return queryVappNetworks(vapp.client, allValues) 352 } 353 354 // QueryVappNetworks returns all vApp networks belonging to the current vApp 355 func (vapp *VApp) QueryVappNetworks(values map[string]string) ([]*types.QueryResultVappNetworkRecordType, error) { 356 // Note: when querying a field that contains a UUID, the system compares only the UUIDs, even if the full field contains more than that. 357 allValues := map[string]string{ 358 "vApp": extractUuid(vapp.VApp.ID), 359 "isLinked": "false", 360 } 361 for k, v := range values { 362 allValues[k] = v 363 } 364 return queryVappNetworks(vapp.client, allValues) 365 } 366 367 // QueryVappOrgNetworks returns all vApp networks belonging to the current vApp 368 func (vapp *VApp) QueryVappOrgNetworks(values map[string]string) ([]*types.QueryResultVappNetworkRecordType, error) { 369 // Note: when querying a field that contains a UUID, the system compares only the UUIDs, even if the full field contains more than that. 370 allValues := map[string]string{ 371 "vApp": extractUuid(vapp.VApp.ID), 372 "isLinked": "true", 373 } 374 for k, v := range values { 375 allValues[k] = v 376 } 377 return queryVappNetworks(vapp.client, allValues) 378 }