github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/opc/resource_instance.go (about) 1 package opc 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "log" 8 "strconv" 9 "strings" 10 11 "github.com/hashicorp/go-oracle-terraform/compute" 12 "github.com/hashicorp/terraform/helper/hashcode" 13 "github.com/hashicorp/terraform/helper/schema" 14 "github.com/hashicorp/terraform/helper/validation" 15 ) 16 17 func resourceInstance() *schema.Resource { 18 return &schema.Resource{ 19 Create: resourceInstanceCreate, 20 Read: resourceInstanceRead, 21 Delete: resourceInstanceDelete, 22 Importer: &schema.ResourceImporter{ 23 State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { 24 combined := strings.Split(d.Id(), "/") 25 if len(combined) != 2 { 26 return nil, fmt.Errorf("Invalid ID specified. Must be in the form of instance_name/instance_id. Got: %s", d.Id()) 27 } 28 d.Set("name", combined[0]) 29 d.SetId(combined[1]) 30 return []*schema.ResourceData{d}, nil 31 }, 32 }, 33 34 Schema: map[string]*schema.Schema{ 35 ///////////////////////// 36 // Required Attributes // 37 ///////////////////////// 38 "name": { 39 Type: schema.TypeString, 40 Required: true, 41 ForceNew: true, 42 }, 43 44 "shape": { 45 Type: schema.TypeString, 46 Required: true, 47 ForceNew: true, 48 }, 49 50 ///////////////////////// 51 // Optional Attributes // 52 ///////////////////////// 53 "instance_attributes": { 54 Type: schema.TypeString, 55 Optional: true, 56 ForceNew: true, 57 ValidateFunc: validation.ValidateJsonString, 58 }, 59 60 "boot_order": { 61 Type: schema.TypeList, 62 Optional: true, 63 ForceNew: true, 64 Elem: &schema.Schema{Type: schema.TypeInt}, 65 }, 66 67 "hostname": { 68 Type: schema.TypeString, 69 Optional: true, 70 Computed: true, 71 ForceNew: true, 72 }, 73 74 "image_list": { 75 Type: schema.TypeString, 76 Optional: true, 77 ForceNew: true, 78 }, 79 80 "label": { 81 Type: schema.TypeString, 82 Optional: true, 83 Computed: true, 84 ForceNew: true, 85 }, 86 87 "networking_info": { 88 Type: schema.TypeSet, 89 Optional: true, 90 Computed: true, 91 ForceNew: true, 92 Elem: &schema.Resource{ 93 Schema: map[string]*schema.Schema{ 94 "dns": { 95 // Required for Shared Network Interface, will default if unspecified, however 96 // Optional for IP Network Interface 97 Type: schema.TypeList, 98 Optional: true, 99 Computed: true, 100 ForceNew: true, 101 Elem: &schema.Schema{Type: schema.TypeString}, 102 }, 103 104 "index": { 105 Type: schema.TypeInt, 106 ForceNew: true, 107 Required: true, 108 }, 109 110 "ip_address": { 111 // Optional, IP Network only 112 Type: schema.TypeString, 113 ForceNew: true, 114 Optional: true, 115 }, 116 117 "ip_network": { 118 // Required for an IP Network Interface 119 Type: schema.TypeString, 120 ForceNew: true, 121 Optional: true, 122 }, 123 124 "mac_address": { 125 // Optional, IP Network Only 126 Type: schema.TypeString, 127 ForceNew: true, 128 Computed: true, 129 Optional: true, 130 }, 131 132 "name_servers": { 133 // Optional, IP Network + Shared Network 134 Type: schema.TypeList, 135 Optional: true, 136 ForceNew: true, 137 Elem: &schema.Schema{Type: schema.TypeString}, 138 }, 139 140 "nat": { 141 // Optional for IP Network 142 // Required for Shared Network 143 Type: schema.TypeList, 144 Optional: true, 145 ForceNew: true, 146 Elem: &schema.Schema{Type: schema.TypeString}, 147 }, 148 149 "search_domains": { 150 // Optional, IP Network + Shared Network 151 Type: schema.TypeList, 152 Optional: true, 153 ForceNew: true, 154 Elem: &schema.Schema{Type: schema.TypeString}, 155 }, 156 157 "sec_lists": { 158 // Required, Shared Network only. Will default if unspecified however 159 Type: schema.TypeList, 160 Optional: true, 161 Computed: true, 162 ForceNew: true, 163 Elem: &schema.Schema{Type: schema.TypeString}, 164 }, 165 166 "shared_network": { 167 Type: schema.TypeBool, 168 Optional: true, 169 ForceNew: true, 170 Default: false, 171 }, 172 173 "vnic": { 174 // Optional, IP Network only. 175 Type: schema.TypeString, 176 ForceNew: true, 177 Optional: true, 178 }, 179 180 "vnic_sets": { 181 // Optional, IP Network only. 182 Type: schema.TypeList, 183 Optional: true, 184 ForceNew: true, 185 Elem: &schema.Schema{Type: schema.TypeString}, 186 }, 187 }, 188 }, 189 Set: func(v interface{}) int { 190 var buf bytes.Buffer 191 m := v.(map[string]interface{}) 192 buf.WriteString(fmt.Sprintf("%d-", m["index"].(int))) 193 buf.WriteString(fmt.Sprintf("%s-", m["vnic"].(string))) 194 buf.WriteString(fmt.Sprintf("%s-", m["nat"])) 195 return hashcode.String(buf.String()) 196 }, 197 }, 198 199 "reverse_dns": { 200 Type: schema.TypeBool, 201 Optional: true, 202 Default: true, 203 ForceNew: true, 204 }, 205 206 "ssh_keys": { 207 Type: schema.TypeList, 208 Optional: true, 209 ForceNew: true, 210 Elem: &schema.Schema{Type: schema.TypeString}, 211 }, 212 213 "storage": { 214 Type: schema.TypeSet, 215 Optional: true, 216 ForceNew: true, 217 Elem: &schema.Resource{ 218 Schema: map[string]*schema.Schema{ 219 "index": { 220 Type: schema.TypeInt, 221 Required: true, 222 ForceNew: true, 223 ValidateFunc: validation.IntBetween(1, 10), 224 }, 225 "volume": { 226 Type: schema.TypeString, 227 Required: true, 228 ForceNew: true, 229 }, 230 "name": { 231 Type: schema.TypeString, 232 Computed: true, 233 }, 234 }, 235 }, 236 }, 237 238 "tags": tagsForceNewSchema(), 239 240 ///////////////////////// 241 // Computed Attributes // 242 ///////////////////////// 243 "attributes": { 244 Type: schema.TypeString, 245 Computed: true, 246 }, 247 248 "availability_domain": { 249 Type: schema.TypeString, 250 Computed: true, 251 }, 252 253 "domain": { 254 Type: schema.TypeString, 255 Computed: true, 256 }, 257 258 "entry": { 259 Type: schema.TypeInt, 260 Computed: true, 261 }, 262 263 "fingerprint": { 264 Type: schema.TypeString, 265 Computed: true, 266 }, 267 268 "image_format": { 269 Type: schema.TypeString, 270 Computed: true, 271 }, 272 273 "ip_address": { 274 Type: schema.TypeString, 275 Computed: true, 276 }, 277 278 "placement_requirements": { 279 Type: schema.TypeList, 280 Computed: true, 281 Elem: &schema.Schema{Type: schema.TypeString}, 282 }, 283 284 "platform": { 285 Type: schema.TypeString, 286 Computed: true, 287 }, 288 289 "priority": { 290 Type: schema.TypeString, 291 Computed: true, 292 }, 293 294 "quota_reservation": { 295 Type: schema.TypeString, 296 Computed: true, 297 }, 298 299 "relationships": { 300 Type: schema.TypeList, 301 Computed: true, 302 Elem: &schema.Schema{Type: schema.TypeString}, 303 }, 304 305 "resolvers": { 306 Type: schema.TypeList, 307 Computed: true, 308 Elem: &schema.Schema{Type: schema.TypeString}, 309 }, 310 311 "site": { 312 Type: schema.TypeString, 313 Computed: true, 314 }, 315 316 "start_time": { 317 Type: schema.TypeString, 318 Computed: true, 319 }, 320 321 "state": { 322 Type: schema.TypeString, 323 Computed: true, 324 }, 325 326 "vcable": { 327 Type: schema.TypeString, 328 Computed: true, 329 }, 330 331 "virtio": { 332 Type: schema.TypeBool, 333 Computed: true, 334 }, 335 336 "vnc_address": { 337 Type: schema.TypeString, 338 Computed: true, 339 }, 340 }, 341 } 342 } 343 344 func resourceInstanceCreate(d *schema.ResourceData, meta interface{}) error { 345 client := meta.(*compute.Client).Instances() 346 347 // Get Required Attributes 348 input := &compute.CreateInstanceInput{ 349 Name: d.Get("name").(string), 350 Shape: d.Get("shape").(string), 351 } 352 353 // Get optional instance attributes 354 attributes, attrErr := getInstanceAttributes(d) 355 if attrErr != nil { 356 return attrErr 357 } 358 359 if attributes != nil { 360 input.Attributes = attributes 361 } 362 363 if bootOrder := getIntList(d, "boot_order"); len(bootOrder) > 0 { 364 input.BootOrder = bootOrder 365 } 366 367 if v, ok := d.GetOk("hostname"); ok { 368 input.Hostname = v.(string) 369 } 370 371 if v, ok := d.GetOk("image_list"); ok { 372 input.ImageList = v.(string) 373 } 374 375 if v, ok := d.GetOk("label"); ok { 376 input.Label = v.(string) 377 } 378 379 interfaces, err := readNetworkInterfacesFromConfig(d) 380 if err != nil { 381 return err 382 } 383 if interfaces != nil { 384 input.Networking = interfaces 385 } 386 387 if v, ok := d.GetOk("reverse_dns"); ok { 388 input.ReverseDNS = v.(bool) 389 } 390 391 if sshKeys := getStringList(d, "ssh_keys"); len(sshKeys) > 0 { 392 input.SSHKeys = sshKeys 393 } 394 395 storage := getStorageAttachments(d) 396 if len(storage) > 0 { 397 input.Storage = storage 398 } 399 400 if tags := getStringList(d, "tags"); len(tags) > 0 { 401 input.Tags = tags 402 } 403 404 result, err := client.CreateInstance(input) 405 if err != nil { 406 return fmt.Errorf("Error creating instance %s: %s", input.Name, err) 407 } 408 409 log.Printf("[DEBUG] Created instance %s: %#v", input.Name, result.ID) 410 411 d.SetId(result.ID) 412 413 return resourceInstanceRead(d, meta) 414 } 415 416 func resourceInstanceRead(d *schema.ResourceData, meta interface{}) error { 417 client := meta.(*compute.Client).Instances() 418 419 name := d.Get("name").(string) 420 421 input := &compute.GetInstanceInput{ 422 ID: d.Id(), 423 Name: name, 424 } 425 426 log.Printf("[DEBUG] Reading state of instance %s", name) 427 result, err := client.GetInstance(input) 428 if err != nil { 429 // Instance doesn't exist 430 if compute.WasNotFoundError(err) { 431 log.Printf("[DEBUG] Instance %s not found", name) 432 d.SetId("") 433 return nil 434 } 435 return fmt.Errorf("Error reading instance %s: %s", name, err) 436 } 437 438 log.Printf("[DEBUG] Instance '%s' found", name) 439 440 // Update attributes 441 return updateInstanceAttributes(d, result) 442 } 443 444 func updateInstanceAttributes(d *schema.ResourceData, instance *compute.InstanceInfo) error { 445 d.Set("name", instance.Name) 446 d.Set("shape", instance.Shape) 447 448 if err := setInstanceAttributes(d, instance.Attributes); err != nil { 449 return err 450 } 451 452 if attrs, ok := d.GetOk("instance_attributes"); ok && attrs != nil { 453 d.Set("instance_attributes", attrs.(string)) 454 } 455 456 if err := setIntList(d, "boot_order", instance.BootOrder); err != nil { 457 return err 458 } 459 d.Set("hostname", instance.Hostname) 460 d.Set("image_list", instance.ImageList) 461 d.Set("label", instance.Label) 462 463 if err := readNetworkInterfaces(d, instance.Networking); err != nil { 464 return err 465 } 466 467 d.Set("reverse_dns", instance.ReverseDNS) 468 if err := setStringList(d, "ssh_keys", instance.SSHKeys); err != nil { 469 return err 470 } 471 472 if err := readStorageAttachments(d, instance.Storage); err != nil { 473 return err 474 } 475 476 if err := setStringList(d, "tags", instance.Tags); err != nil { 477 return err 478 } 479 d.Set("availability_domain", instance.AvailabilityDomain) 480 d.Set("domain", instance.Domain) 481 d.Set("entry", instance.Entry) 482 d.Set("fingerprint", instance.Fingerprint) 483 d.Set("image_format", instance.ImageFormat) 484 d.Set("ip_address", instance.IPAddress) 485 486 if err := setStringList(d, "placement_requirements", instance.PlacementRequirements); err != nil { 487 return err 488 } 489 490 d.Set("platform", instance.Platform) 491 d.Set("priority", instance.Priority) 492 d.Set("quota_reservation", instance.QuotaReservation) 493 494 if err := setStringList(d, "relationships", instance.Relationships); err != nil { 495 return err 496 } 497 498 if err := setStringList(d, "resolvers", instance.Resolvers); err != nil { 499 return err 500 } 501 502 d.Set("site", instance.Site) 503 d.Set("start_time", instance.StartTime) 504 d.Set("state", instance.State) 505 506 if err := setStringList(d, "tags", instance.Tags); err != nil { 507 return err 508 } 509 510 d.Set("vcable", instance.VCableID) 511 d.Set("virtio", instance.Virtio) 512 d.Set("vnc_address", instance.VNC) 513 514 return nil 515 } 516 517 func resourceInstanceDelete(d *schema.ResourceData, meta interface{}) error { 518 client := meta.(*compute.Client).Instances() 519 520 name := d.Get("name").(string) 521 522 input := &compute.DeleteInstanceInput{ 523 ID: d.Id(), 524 Name: name, 525 } 526 log.Printf("[DEBUG] Deleting instance %s", name) 527 528 if err := client.DeleteInstance(input); err != nil { 529 return fmt.Errorf("Error deleting instance %s: %s", name, err) 530 } 531 532 return nil 533 } 534 535 func getStorageAttachments(d *schema.ResourceData) []compute.StorageAttachmentInput { 536 storageAttachments := []compute.StorageAttachmentInput{} 537 storage := d.Get("storage").(*schema.Set) 538 for _, i := range storage.List() { 539 attrs := i.(map[string]interface{}) 540 storageAttachments = append(storageAttachments, compute.StorageAttachmentInput{ 541 Index: attrs["index"].(int), 542 Volume: attrs["volume"].(string), 543 }) 544 } 545 return storageAttachments 546 } 547 548 // Parses instance_attributes from a string to a map[string]interface and returns any errors. 549 func getInstanceAttributes(d *schema.ResourceData) (map[string]interface{}, error) { 550 var attrs map[string]interface{} 551 552 // Empty instance attributes 553 attributes, ok := d.GetOk("instance_attributes") 554 if !ok { 555 return attrs, nil 556 } 557 558 if err := json.Unmarshal([]byte(attributes.(string)), &attrs); err != nil { 559 return attrs, fmt.Errorf("Cannot parse attributes as json: %s", err) 560 } 561 562 return attrs, nil 563 } 564 565 // Reads attributes from the returned instance object, and sets the computed attributes string 566 // as JSON 567 func setInstanceAttributes(d *schema.ResourceData, attributes map[string]interface{}) error { 568 // Shouldn't ever get nil attributes on an instance, but protect against the case either way 569 if attributes == nil { 570 return nil 571 } 572 573 b, err := json.Marshal(attributes) 574 if err != nil { 575 return fmt.Errorf("Error marshalling returned attributes: %s", err) 576 } 577 return d.Set("attributes", string(b)) 578 } 579 580 // Populates and validates shared network and ip network interfaces to return the of map 581 // objects needed to create/update an instance's networking_info 582 func readNetworkInterfacesFromConfig(d *schema.ResourceData) (map[string]compute.NetworkingInfo, error) { 583 interfaces := make(map[string]compute.NetworkingInfo) 584 585 if v, ok := d.GetOk("networking_info"); ok { 586 vL := v.(*schema.Set).List() 587 for _, v := range vL { 588 ni := v.(map[string]interface{}) 589 index, ok := ni["index"].(int) 590 if !ok { 591 return nil, fmt.Errorf("Index not specified for network interface: %v", ni) 592 } 593 594 deviceIndex := fmt.Sprintf("eth%d", index) 595 596 // Verify that the network interface doesn't already exist 597 if _, ok := interfaces[deviceIndex]; ok { 598 return nil, fmt.Errorf("Duplicate Network interface at eth%d already specified", index) 599 } 600 601 // Determine if we're creating a shared network interface or an IP Network interface 602 info := compute.NetworkingInfo{} 603 var err error 604 if ni["shared_network"].(bool) { 605 // Populate shared network parameters 606 info, err = readSharedNetworkFromConfig(ni) 607 // Set 'model' since we're configuring a shared network interface 608 info.Model = compute.NICDefaultModel 609 } else { 610 // Populate IP Network Parameters 611 info, err = readIPNetworkFromConfig(ni) 612 } 613 if err != nil { 614 return nil, err 615 } 616 // And you may find yourself in a beautiful house, with a beautiful wife 617 // And you may ask yourself, well, how did I get here? 618 interfaces[deviceIndex] = info 619 } 620 } 621 622 return interfaces, nil 623 } 624 625 // Reads a networking_info config block as a shared network interface 626 func readSharedNetworkFromConfig(ni map[string]interface{}) (compute.NetworkingInfo, error) { 627 info := compute.NetworkingInfo{} 628 // Validate the shared network 629 if err := validateSharedNetwork(ni); err != nil { 630 return info, err 631 } 632 // Populate shared network fields; checking type casting 633 dns := []string{} 634 if v, ok := ni["dns"]; ok && v != nil { 635 for _, d := range v.([]interface{}) { 636 dns = append(dns, d.(string)) 637 } 638 if len(dns) > 0 { 639 info.DNS = dns 640 } 641 } 642 643 if v, ok := ni["model"].(string); ok && v != "" { 644 info.Model = compute.NICModel(v) 645 } 646 647 nats := []string{} 648 if v, ok := ni["nat"]; ok && v != nil { 649 for _, nat := range v.([]interface{}) { 650 nats = append(nats, nat.(string)) 651 } 652 if len(nats) > 0 { 653 info.Nat = nats 654 } 655 } 656 657 slists := []string{} 658 if v, ok := ni["sec_lists"]; ok && v != nil { 659 for _, slist := range v.([]interface{}) { 660 slists = append(slists, slist.(string)) 661 } 662 if len(slists) > 0 { 663 info.SecLists = slists 664 } 665 } 666 667 nservers := []string{} 668 if v, ok := ni["name_servers"]; ok && v != nil { 669 for _, nserver := range v.([]interface{}) { 670 nservers = append(nservers, nserver.(string)) 671 } 672 if len(nservers) > 0 { 673 info.NameServers = nservers 674 } 675 } 676 677 sdomains := []string{} 678 if v, ok := ni["search_domains"]; ok && v != nil { 679 for _, sdomain := range v.([]interface{}) { 680 sdomains = append(sdomains, sdomain.(string)) 681 } 682 if len(sdomains) > 0 { 683 info.SearchDomains = sdomains 684 } 685 } 686 687 return info, nil 688 } 689 690 // Unfortunately this cannot take place during plan-phase, because we currently cannot have a validation 691 // function based off of multiple fields in the supplied schema. 692 func validateSharedNetwork(ni map[string]interface{}) error { 693 // A Shared Networking Interface MUST have the following attributes set: 694 // - "nat" 695 // The following attributes _cannot_ be set for a shared network: 696 // - "ip_address" 697 // - "ip_network" 698 // - "mac_address" 699 // - "vnic" 700 // - "vnic_sets" 701 702 if _, ok := ni["nat"]; !ok { 703 return fmt.Errorf("'nat' field needs to be set for a Shared Networking Interface") 704 } 705 706 // Strings only 707 nilAttrs := []string{ 708 "ip_address", 709 "ip_network", 710 "mac_address", 711 "vnic", 712 } 713 714 for _, v := range nilAttrs { 715 if d, ok := ni[v]; ok && d.(string) != "" { 716 return fmt.Errorf("%q field cannot be set in a Shared Networking Interface", v) 717 } 718 } 719 if _, ok := ni["vnic_sets"].([]string); ok { 720 return fmt.Errorf("%q field cannot be set in a Shared Networking Interface", "vnic_sets") 721 } 722 723 return nil 724 } 725 726 // Populates fields for an IP Network 727 func readIPNetworkFromConfig(ni map[string]interface{}) (compute.NetworkingInfo, error) { 728 info := compute.NetworkingInfo{} 729 // Validate the IP Network 730 if err := validateIPNetwork(ni); err != nil { 731 return info, err 732 } 733 // Populate fields 734 if v, ok := ni["ip_network"].(string); ok && v != "" { 735 info.IPNetwork = v 736 } 737 738 dns := []string{} 739 if v, ok := ni["dns"]; ok && v != nil { 740 for _, d := range v.([]interface{}) { 741 dns = append(dns, d.(string)) 742 } 743 if len(dns) > 0 { 744 info.DNS = dns 745 } 746 } 747 748 if v, ok := ni["ip_address"].(string); ok && v != "" { 749 info.IPAddress = v 750 } 751 752 if v, ok := ni["mac_address"].(string); ok && v != "" { 753 info.MACAddress = v 754 } 755 756 nservers := []string{} 757 if v, ok := ni["name_servers"]; ok && v != nil { 758 for _, nserver := range v.([]interface{}) { 759 nservers = append(nservers, nserver.(string)) 760 } 761 if len(nservers) > 0 { 762 info.NameServers = nservers 763 } 764 } 765 766 nats := []string{} 767 if v, ok := ni["nat"]; ok && v != nil { 768 for _, nat := range v.([]interface{}) { 769 nats = append(nats, nat.(string)) 770 } 771 if len(nats) > 0 { 772 info.Nat = nats 773 } 774 } 775 776 sdomains := []string{} 777 if v, ok := ni["search_domains"]; ok && v != nil { 778 for _, sdomain := range v.([]interface{}) { 779 sdomains = append(sdomains, sdomain.(string)) 780 } 781 if len(sdomains) > 0 { 782 info.SearchDomains = sdomains 783 } 784 } 785 786 if v, ok := ni["vnic"].(string); ok && v != "" { 787 info.Vnic = v 788 } 789 790 vnicSets := []string{} 791 if v, ok := ni["vnic_sets"]; ok && v != nil { 792 for _, vnic := range v.([]interface{}) { 793 vnicSets = append(vnicSets, vnic.(string)) 794 } 795 if len(vnicSets) > 0 { 796 info.VnicSets = vnicSets 797 } 798 } 799 800 return info, nil 801 } 802 803 // Validates an IP Network config block 804 func validateIPNetwork(ni map[string]interface{}) error { 805 // An IP Networking Interface MUST have the following attributes set: 806 // - "ip_network" 807 808 // Required to be set 809 if d, ok := ni["ip_network"]; !ok || d.(string) == "" { 810 return fmt.Errorf("'ip_network' field is required for an IP Network interface") 811 } 812 813 return nil 814 } 815 816 // Reads network interfaces from the config 817 func readNetworkInterfaces(d *schema.ResourceData, ifaces map[string]compute.NetworkingInfo) error { 818 result := make([]map[string]interface{}, 0) 819 820 // Nil check for import case 821 if ifaces == nil { 822 return d.Set("networking_info", result) 823 } 824 825 for index, iface := range ifaces { 826 res := make(map[string]interface{}) 827 // The index returned from the SDK holds the full device_index from the instance. 828 // For users convenience, we simply allow them to specify the integer equivalent of the device_index 829 // so a user could implement several network interfaces via `count`. 830 // Convert the full device_index `ethN` to `N` as an integer. 831 index := strings.TrimPrefix(index, "eth") 832 indexInt, err := strconv.Atoi(index) 833 if err != nil { 834 return err 835 } 836 res["index"] = indexInt 837 838 // Set the proper attributes for this specific network interface 839 if iface.DNS != nil { 840 res["dns"] = iface.DNS 841 } 842 if iface.IPAddress != "" { 843 res["ip_address"] = iface.IPAddress 844 } 845 if iface.IPNetwork != "" { 846 res["ip_network"] = iface.IPNetwork 847 } 848 if iface.MACAddress != "" { 849 res["mac_address"] = iface.MACAddress 850 } 851 if iface.Model != "" { 852 // Model can only be set on Shared networks 853 res["shared_network"] = true 854 } 855 if iface.NameServers != nil { 856 res["name_servers"] = iface.NameServers 857 } 858 if iface.Nat != nil { 859 res["nat"] = iface.Nat 860 } 861 if iface.SearchDomains != nil { 862 res["search_domains"] = iface.SearchDomains 863 } 864 if iface.SecLists != nil { 865 res["sec_lists"] = iface.SecLists 866 } 867 if iface.Vnic != "" { 868 res["vnic"] = iface.Vnic 869 // VNIC can only be set on an IP Network 870 res["shared_network"] = false 871 } 872 if iface.VnicSets != nil { 873 res["vnic_sets"] = iface.VnicSets 874 } 875 876 result = append(result, res) 877 } 878 879 return d.Set("networking_info", result) 880 } 881 882 // Flattens the returned slice of storage attachments to a map 883 func readStorageAttachments(d *schema.ResourceData, attachments []compute.StorageAttachment) error { 884 result := make([]map[string]interface{}, 0) 885 886 if attachments == nil || len(attachments) == 0 { 887 return d.Set("storage", nil) 888 } 889 890 for _, attachment := range attachments { 891 res := make(map[string]interface{}) 892 res["index"] = attachment.Index 893 res["volume"] = attachment.StorageVolumeName 894 res["name"] = attachment.Name 895 result = append(result, res) 896 } 897 return d.Set("storage", result) 898 }