This repository has been archived by the owner on Aug 12, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
compute_deh_v1_hosts.go
235 lines (203 loc) · 7.8 KB
/
compute_deh_v1_hosts.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
// This set of code handles all functions required to configure networking
// on an opentelekomcloud_compute_instance_v2 resource.
//
// This is a complicated task because it's not possible to obtain all
// information in a single API call. In fact, it even traverses multiple
// OpenTelekomCloud services.
//
// The end result, from the user's point of view, is a structured set of
// understandable network information within the instance resource.
package opentelekomcloud
import (
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/huaweicloud/golangsdk/openstack/compute/v2/servers"
"log"
)
// InstanceNICS is a structured representation of a Gophercloud servers.Server
// virtual NIC.
type InstanceNICS struct {
FixedIPv4 string
FixedIPv6 string
}
// InstanceAddresses is a collection of InstanceNICs, grouped by the
// network name. An instance/server could have multiple NICs on the same
// network.
type InstancesAddress struct {
NetworkName string
InstanceNICs []InstanceNICS
}
// InstanceNetwork represents a collection of network information that a
// Terraform instance needs to satisfy all network information requirements.
type InstanceNetworks struct {
UUID string
Name string
Port string
FixedIP string
AccessNetwork bool
}
// getAllInstanceNetworks loops through the networks defined in the Terraform
// configuration and structures that information into something standard that
// can be consumed by both OpenTelekomCloud and Terraform.
//
// This would be simple, except we have ensure both the network name and
// network ID have been determined. This isn't just for the convenience of a
// user specifying a human-readable network name, but the network information
// returned by an OpenTelekomCloud instance only has the network name set! So if a
// user specified a network ID, there's no way to correlate it to the instance
// unless we know both the name and ID.
//
// Not only that, but we have to account for two OpenTelekomCloud network services
// running: nova-network (legacy) and Neutron (current).
//
// In addition, if a port was specified, not all of the port information
// will be displayed, such as multiple fixed and floating IPs. This resource
// isn't currently configured for that type of flexibility. It's better to
// reference the actual port resource itself.
//
// So, let's begin the journey.
func getAllInstanceNetwork(d *schema.ResourceData, meta interface{}) ([]InstanceNetworks, error) {
var instanceNetworks []InstanceNetworks
networks := d.Get("addresses").([]interface{})
for _, v := range networks {
network := v.(map[string]interface{})
networkID := network["uuid"].(string)
networkName := network["name"].(string)
portID := network["port"].(string)
if networkID == "" && networkName == "" && portID == "" {
return nil, fmt.Errorf(
"At least one of network.uuid, network.name, or network.port must be set.")
}
// If a user specified both an ID and name, that makes things easy
// since both name and ID are already satisfied. No need to query
// further.
if networkID != "" && networkName != "" {
v := InstanceNetworks{
UUID: networkID,
Name: networkName,
Port: portID,
FixedIP: network["fixed_ip_v4"].(string),
AccessNetwork: network["access_network"].(bool),
}
instanceNetworks = append(instanceNetworks, v)
continue
}
// But if at least one of name or ID was missing, we have to query
// for that other piece.
//
// Priority is given to a port since a network ID or name usually isn't
// specified when using a port.
//
// Next priority is given to the network ID since it's guaranteed to be
// an exact match.
queryType := "name"
queryTerm := networkName
if networkID != "" {
queryType = "id"
queryTerm = networkID
}
if portID != "" {
queryType = "port"
queryTerm = portID
}
networkInfo, err := getInstanceNetworkInfo(d, meta, queryType, queryTerm)
if err != nil {
return nil, err
}
v := InstanceNetworks{
UUID: networkInfo["uuid"].(string),
Name: networkInfo["name"].(string),
Port: portID,
FixedIP: network["fixed_ip_v4"].(string),
AccessNetwork: network["access_network"].(bool),
}
instanceNetworks = append(instanceNetworks, v)
}
log.Printf("[DEBUG] getAllInstanceNetworks: %#v", instanceNetworks)
return instanceNetworks, nil
}
// getInstanceAddresses parses a Gophercloud server.Server's Address field into
// a structured InstanceAddresses struct.
func getInstancesAddress(addresses map[string]interface{}) []InstancesAddress {
var allInstanceAddresses []InstancesAddress
for networkName, v := range addresses {
instanceAddresses := InstancesAddress{
NetworkName: networkName,
}
instanceNIC := InstanceNICS{}
for _, v := range v.([]interface{}) {
v := v.(map[string]interface{})
if v["OS-EXT-IPS:type"] == "fixed" {
switch v["version"].(float64) {
case 6:
instanceNIC.FixedIPv6 = fmt.Sprintf("[%s]", v["addr"].(string))
default:
instanceNIC.FixedIPv4 = v["addr"].(string)
}
}
instanceAddresses.InstanceNICs = append(instanceAddresses.InstanceNICs, instanceNIC)
}
allInstanceAddresses = append(allInstanceAddresses, instanceAddresses)
}
log.Printf("[DEBUG] Addresses: %#v", addresses)
log.Printf("[DEBUG] allInstanceAddresses: %#v", allInstanceAddresses)
return allInstanceAddresses
}
// flattenInstanceNetworks collects instance network information from different
// sources and aggregates it all together into a map array.
func flattenInstanceNetwork(d *schema.ResourceData, meta interface{}) ([]map[string]interface{}, error) {
config := meta.(*Config)
computeClient, err := config.computeV2Client(GetRegion(d, config))
if err != nil {
return nil, fmt.Errorf("Error creating OpenTelekomCloud compute client: %s", err)
}
server, err := servers.Get(computeClient, d.Id()).Extract()
if err != nil {
return nil, CheckDeleted(d, err, "server")
}
allInstanceAddresses := getInstancesAddress(server.Addresses)
allInstanceNetworks, err := getAllInstanceNetwork(d, meta)
if err != nil {
return nil, err
}
networks := []map[string]interface{}{}
// If there were no instance networks returned, this means that there
// was not a network specified in the Terraform configuration. When this
// happens, the instance will be launched on a "default" network, if one
// is available. If there isn't, the instance will fail to launch, so
// this is a safe assumption at this point.
if len(allInstanceNetworks) == 0 {
for _, instanceAddresses := range allInstanceAddresses {
for _, instanceNIC := range instanceAddresses.InstanceNICs {
v := map[string]interface{}{
"name": instanceAddresses.NetworkName,
"fixed_ip_v4": instanceNIC.FixedIPv4,
}
networks = append(networks, v)
}
}
log.Printf("[DEBUG] flattenInstanceNetworks: %#v", networks)
return networks, nil
}
// Loop through all networks and addresses, merge relevant address details.
for _, instanceNetwork := range allInstanceNetworks {
for _, instanceAddresses := range allInstanceAddresses {
if instanceNetwork.Name == instanceAddresses.NetworkName {
// Only use one NIC since it's possible the user defined another NIC
// on this same network in another Terraform network block.
instanceNIC := instanceAddresses.InstanceNICs[0]
copy(instanceAddresses.InstanceNICs, instanceAddresses.InstanceNICs[1:])
v := map[string]interface{}{
"name": instanceAddresses.NetworkName,
"fixed_ip_v4": instanceNIC.FixedIPv4,
"uuid": instanceNetwork.UUID,
"port": instanceNetwork.Port,
"access_network": instanceNetwork.AccessNetwork,
}
networks = append(networks, v)
}
}
}
log.Printf("[DEBUG] flattenInstanceNetworks: %#v", networks)
return networks, nil
}