Skip to content

Commit 7a13658

Browse files
authored
Introduce source_pif_device (replacement for pif_id) to better match TF's mental model fix import issues (#260)
* Replace problematic pif_id argument with source_pif_device to address network resource import issues Signed-off-by: Dom Del Nano <[email protected]> * Update network resource docs * Fix pif filtering Signed-off-by: Dom Del Nano <[email protected]> --------- Signed-off-by: Dom Del Nano <[email protected]>
1 parent 8a0b0cd commit 7a13658

File tree

5 files changed

+131
-41
lines changed

5 files changed

+131
-41
lines changed

client/pif.go

+12-3
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,24 @@ type PIF struct {
2020
func (p PIF) Compare(obj interface{}) bool {
2121
otherPif := obj.(PIF)
2222

23-
if p.Id != "" && otherPif.Id == p.Id {
24-
return true
23+
if p.Id != "" {
24+
if otherPif.Id == p.Id {
25+
return true
26+
} else {
27+
return false
28+
}
2529
}
2630
hostIdExists := p.Host != ""
2731
if hostIdExists && p.Host != otherPif.Host {
2832
return false
2933
}
3034

31-
if p.Vlan == otherPif.Vlan && p.Device == otherPif.Device {
35+
networkIdExists := p.Network != ""
36+
if networkIdExists && p.Network != otherPif.Network {
37+
return false
38+
}
39+
40+
if p.Vlan == otherPif.Vlan && (p.Device == "" || (p.Device == otherPif.Device)) {
3241
return true
3342
}
3443
return false

docs/resources/network.md

+8-7
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,17 @@ data "xenorchestra_host" "host1" {
1717
name_label = "Your host"
1818
}
1919
20-
data "xenorchestra_pif" "pif" {
21-
device = "eth0"
22-
vlan = -1
23-
host_id = data.xenorchestra_host.host1.id
20+
# Create a single server network private network
21+
resource "xenorchestra_network" "private_network" {
22+
name_label = "new network name"
23+
pool_id = data.xenorchestra_host.host1.pool_id
2424
}
2525
26-
resource "xenorchestra_network" "network" {
26+
# Create a network with a 22 VLAN tag from the eth0 device
27+
resource "xenorchestra_network" "vlan_network" {
2728
name_label = "new network name"
2829
pool_id = data.xenorchestra_host.host1.pool_id
29-
pif_id = data.xenorchestra_pif.pif.id
30+
source_pif_device = "eth0"
3031
vlan = 22
3132
}
3233
```
@@ -46,7 +47,7 @@ resource "xenorchestra_network" "network" {
4647
- `mtu` (Number) The MTU of the network. Defaults to `1500` if unspecified.
4748
- `name_description` (String)
4849
- `nbd` (Boolean) Whether the network should use a network block device. Defaults to `false` if unspecified.
49-
- `pif_id` (String) The pif (uuid) that should be used for this network.
50+
- `source_pif_device` (String) The PIF device (eth0, eth1, etc) that will be used as an input during network creation. This parameter is required if a vlan is specified.
5051
- `vlan` (Number) The vlan to use for the network. Defaults to `0` meaning no VLAN.
5152

5253
### Read-Only

examples/resources/xenorchestra_network/resource.tf

+7-6
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@ data "xenorchestra_host" "host1" {
22
name_label = "Your host"
33
}
44

5-
data "xenorchestra_pif" "pif" {
6-
device = "eth0"
7-
vlan = -1
8-
host_id = data.xenorchestra_host.host1.id
5+
# Create a single server network private network
6+
resource "xenorchestra_network" "private_network" {
7+
name_label = "new network name"
8+
pool_id = data.xenorchestra_host.host1.pool_id
99
}
1010

11-
resource "xenorchestra_network" "network" {
11+
# Create a network with a 22 VLAN tag from the eth0 device
12+
resource "xenorchestra_network" "vlan_network" {
1213
name_label = "new network name"
1314
pool_id = data.xenorchestra_host.host1.pool_id
14-
pif_id = data.xenorchestra_pif.pif.id
15+
source_pif_device = "eth0"
1516
vlan = 22
1617
}

xoa/resource_xenorchestra_network.go

+62-14
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package xoa
22

33
import (
44
"errors"
5+
"fmt"
6+
"log"
57

68
"github.com/ddelnano/terraform-provider-xenorchestra/client"
79
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@@ -40,21 +42,21 @@ func resourceXoaNetwork() *schema.Resource {
4042
Optional: true,
4143
Default: netDefaultDesc,
4244
},
43-
"pif_id": &schema.Schema{
45+
"source_pif_device": &schema.Schema{
4446
Type: schema.TypeString,
4547
Optional: true,
4648
RequiredWith: []string{
4749
"vlan",
4850
},
4951
ForceNew: true,
50-
Description: "The pif (uuid) that should be used for this network.",
52+
Description: "The PIF device (eth0, eth1, etc) that will be used as an input during network creation. This parameter is required if a vlan is specified.",
5153
},
5254
"vlan": &schema.Schema{
5355
Type: schema.TypeInt,
5456
Optional: true,
5557
Default: 0,
5658
RequiredWith: []string{
57-
"pif_id",
59+
"source_pif_device",
5860
},
5961
ForceNew: true,
6062
Description: "The vlan to use for the network. Defaults to `0` meaning no VLAN.",
@@ -85,6 +87,16 @@ func resourceXoaNetwork() *schema.Resource {
8587
func resourceNetworkCreate(d *schema.ResourceData, m interface{}) error {
8688
c := m.(client.XOClient)
8789

90+
var pifId string
91+
if sourcePIFDevice := d.Get("source_pif_device").(string); sourcePIFDevice != "" {
92+
pif, err := getNetworkCreationSourcePIF(c, sourcePIFDevice, d.Get("pool_id").(string))
93+
94+
if err != nil {
95+
return err
96+
}
97+
pifId = pif.Id
98+
}
99+
88100
network, err := c.CreateNetwork(client.CreateNetworkRequest{
89101
Automatic: d.Get("automatic").(bool),
90102
DefaultIsLocked: d.Get("default_is_locked").(bool),
@@ -94,31 +106,63 @@ func resourceNetworkCreate(d *schema.ResourceData, m interface{}) error {
94106
Mtu: d.Get("mtu").(int),
95107
Nbd: d.Get("nbd").(bool),
96108
Vlan: d.Get("vlan").(int),
97-
PIF: d.Get("pif_id").(string),
109+
PIF: pifId,
98110
})
99111
if err != nil {
100112
return err
101113
}
102-
vlan, err := getVlanForNetwork(c, network)
114+
vlan, pifDevice, err := getVlanForNetwork(c, network)
103115
if err != nil {
104116
return err
105117
}
106-
return networkToData(network, vlan, d)
118+
return networkToData(network, vlan, pifDevice, d)
107119
}
108120

109-
func getVlanForNetwork(c client.XOClient, net *client.Network) (int, error) {
121+
// This function returns the PIF specified the given device name on the pool's primary host. In order to create
122+
// networks with a VLAN, a PIF for the given device must be provided. Xen Orchestra uses the primary host's PIF
123+
// for this and so we emulate that behavior.
124+
func getNetworkCreationSourcePIF(c client.XOClient, pifDevice, poolId string) (*client.PIF, error) {
125+
pools, err := c.GetPools(client.Pool{Id: poolId})
126+
if err != nil {
127+
return nil, err
128+
}
129+
130+
if len(pools) != 1 {
131+
return nil, errors.New(fmt.Sprintf("expected to find a single pool, instead found %d", len(pools)))
132+
}
133+
134+
pool := pools[0]
135+
pifs, err := c.GetPIF(client.PIF{
136+
Host: pool.Master,
137+
Vlan: -1,
138+
Device: pifDevice,
139+
})
140+
141+
if err != nil {
142+
return nil, err
143+
}
144+
145+
if len(pifs) != 1 {
146+
return nil, errors.New(fmt.Sprintf("expected to find a single PIF, instead found %d. %+v", len(pifs), pifs))
147+
}
148+
149+
return &pifs[0], nil
150+
}
151+
152+
// Returns the VLAN and device name for the given network.
153+
func getVlanForNetwork(c client.XOClient, net *client.Network) (int, string, error) {
110154
if len(net.PIFs) > 0 {
111155
pifs, err := c.GetPIF(client.PIF{Id: net.PIFs[0]})
112156
if err != nil {
113-
return -1, err
157+
return -1, "", err
114158
}
115159

116160
if len(pifs) != 1 {
117-
return -1, errors.New("expected to find single PIF")
161+
return -1, "", errors.New("expected to find single PIF")
118162
}
119-
return pifs[0].Vlan, nil
163+
return pifs[0].Vlan, pifs[0].Device, nil
120164
}
121-
return 0, nil
165+
return 0, "", nil
122166
}
123167

124168
func resourceNetworkRead(d *schema.ResourceData, m interface{}) error {
@@ -135,11 +179,11 @@ func resourceNetworkRead(d *schema.ResourceData, m interface{}) error {
135179
return err
136180
}
137181

138-
vlan, err := getVlanForNetwork(c, net)
182+
vlan, pifDevice, err := getVlanForNetwork(c, net)
139183
if err != nil {
140184
return err
141185
}
142-
return networkToData(net, vlan, d)
186+
return networkToData(net, vlan, pifDevice, d)
143187
}
144188

145189
func resourceNetworkUpdate(d *schema.ResourceData, m interface{}) error {
@@ -178,11 +222,12 @@ func resourceNetworkDelete(d *schema.ResourceData, m interface{}) error {
178222
return nil
179223
}
180224

181-
func networkToData(network *client.Network, vlan int, d *schema.ResourceData) error {
225+
func networkToData(network *client.Network, vlan int, pifDevice string, d *schema.ResourceData) error {
182226
d.SetId(network.Id)
183227
if err := d.Set("name_label", network.NameLabel); err != nil {
184228
return err
185229
}
230+
log.Printf("This is being called\n")
186231
if err := d.Set("name_description", network.NameDescription); err != nil {
187232
return err
188233
}
@@ -204,5 +249,8 @@ func networkToData(network *client.Network, vlan int, d *schema.ResourceData) er
204249
if err := d.Set("vlan", vlan); err != nil {
205250
return err
206251
}
252+
if err := d.Set("source_pif_device", pifDevice); err != nil {
253+
return err
254+
}
207255
return nil
208256
}

xoa/resource_xenorchestra_network_test.go

+42-11
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,42 @@ func TestAccXONetwork_create(t *testing.T) {
3535
)
3636
}
3737

38+
func TestAccXONetwork_import(t *testing.T) {
39+
if accTestPIF.Id == "" {
40+
t.Skip()
41+
}
42+
resourceName := "xenorchestra_network.network"
43+
nameLabel := fmt.Sprintf("%s - %s", accTestPrefix, t.Name())
44+
desc := "Non default description"
45+
nbd := "false"
46+
mtu := "1500"
47+
vlan := "23"
48+
resource.Test(t, resource.TestCase{
49+
PreCheck: func() { testAccPreCheck(t) },
50+
Providers: testAccProviders,
51+
Steps: []resource.TestStep{
52+
{
53+
Config: testAccXenorchestraNetworkConfigNonDefaultsWithVlan(nameLabel, desc, mtu, nbd, vlan),
54+
},
55+
{
56+
ResourceName: resourceName,
57+
ImportState: true,
58+
ImportStateVerify: true,
59+
Check: resource.ComposeAggregateTestCheckFunc(
60+
testAccCheckXenorchestraNetwork(resourceName),
61+
resource.TestCheckResourceAttrSet(resourceName, "id"),
62+
resource.TestCheckResourceAttrSet(resourceName, "name_label"),
63+
resource.TestCheckResourceAttrSet(resourceName, "name_description"),
64+
resource.TestCheckResourceAttrSet(resourceName, "pool_id"),
65+
resource.TestCheckResourceAttrSet(resourceName, "mtu"),
66+
resource.TestCheckResourceAttrSet(resourceName, "source_pif_device"),
67+
resource.TestCheckResourceAttrSet(resourceName, "vlan")),
68+
},
69+
},
70+
},
71+
)
72+
}
73+
3874
func TestAccXONetwork_createWithVlanRequiresPIF(t *testing.T) {
3975
if accTestPIF.Id == "" {
4076
t.Skip()
@@ -46,11 +82,11 @@ func TestAccXONetwork_createWithVlanRequiresPIF(t *testing.T) {
4682
Steps: []resource.TestStep{
4783
{
4884
Config: testAccXenorchestraNetworkConfigWithoutVlan(nameLabel),
49-
ExpectError: regexp.MustCompile("all of `pif_id,vlan` must be specified"),
85+
ExpectError: regexp.MustCompile("all of `source_pif_device,vlan` must be specified"),
5086
},
5187
{
5288
Config: testAccXenorchestraNetworkConfigWithoutPIF(nameLabel),
53-
ExpectError: regexp.MustCompile("all of `pif_id,vlan` must be specified"),
89+
ExpectError: regexp.MustCompile("all of `source_pif_device,vlan` must be specified"),
5490
},
5591
},
5692
},
@@ -80,6 +116,7 @@ func TestAccXONetwork_createWithNonDefaults(t *testing.T) {
80116
resource.TestCheckResourceAttr(resourceName, "name_description", desc),
81117
resource.TestCheckResourceAttr(resourceName, "pool_id", accTestPIF.PoolId),
82118
resource.TestCheckResourceAttr(resourceName, "mtu", mtu),
119+
resource.TestCheckResourceAttrSet(resourceName, "source_pif_device"),
83120
resource.TestCheckResourceAttr(resourceName, "vlan", vlan),
84121
resource.TestCheckResourceAttr(resourceName, "nbd", nbd)),
85122
},
@@ -205,7 +242,7 @@ var testAccXenorchestraNetworkConfigWithoutVlan = func(name string) string {
205242
resource "xenorchestra_network" "network" {
206243
name_label = "%s"
207244
pool_id = "%s"
208-
pif_id = data.xenorchestra_pif.pif.id
245+
source_pif_device = "eth1"
209246
}
210247
`, name, accTestPIF.PoolId)
211248
}
@@ -234,22 +271,16 @@ resource "xenorchestra_network" "network" {
234271

235272
var testAccXenorchestraNetworkConfigNonDefaultsWithVlan = func(name, desc, mtu, nbd, vlan string) string {
236273
return fmt.Sprintf(`
237-
data "xenorchestra_pif" "pif" {
238-
device = "eth0"
239-
vlan = -1
240-
host_id = "%s"
241-
}
242-
243274
resource "xenorchestra_network" "network" {
244275
name_label = "%s"
245276
name_description = "%s"
246277
pool_id = "%s"
247278
mtu = %s
248279
nbd = %s
249-
pif_id = data.xenorchestra_pif.pif.id
280+
source_pif_device = "eth0"
250281
vlan = %s
251282
}
252-
`, accTestPIF.Host, name, desc, accTestPIF.PoolId, mtu, nbd, vlan)
283+
`, name, desc, accTestPIF.PoolId, mtu, nbd, vlan)
253284
}
254285

255286
var testAccXenorchestraNetworkConfigInPlaceUpdates = func(name, desc, nbd, automatic, isLocked string) string {

0 commit comments

Comments
 (0)