terraform-provider-opennebula icon indicating copy to clipboard operation
terraform-provider-opennebula copied to clipboard

Issue with Services: Unable to Create Reserve from Network

Open shurkus opened this issue 1 year ago • 5 comments

Description

I am facing an issue when trying to create a reserve from a network using Terraform. The network reserve is not even initiated; the corresponding step appears to be skipped entirely. However, when I use the same configuration files with the OpenNebula CLI (oneflow-template instantiate 95 talos-extra_template.json), everything works as expected.

Terraform and Provider version

Terraform v1.6.5 on linux_amd64

  • provider registry.terraform.io/opennebula/opennebula v1.4.0 also tried: Terraform v1.5.0 on linux_amd64
  • provider registry.terraform.io/opennebula/opennebula v1.3.1

Affected resources and data sources

opennebula_service

Terraform configuration

./terraform/service.tf

resource "opennebula_service_template" "talos" {
  name        = "talos"
  template    = templatefile("${path.module}/../templates/service_template.json", {})
}

resource "opennebula_service" "talos" {
  name        = "talos"
  template_id = opennebula_service_template.talos.id
  extra_template = jsonencode(
    {
        "custom_attrs_values": {
        },
        "networks_values": [{
          "controlplane": {
            "reserve_from": "1",
            "extra": "NAME=CONTROLPLANE\nSIZE=3"
          },
          "worker": {
            "reserve_from": "1",
            "extra": "NAME=WORKER\nSIZE=5"
          }
        }]

    }
  )
}

./templates/service_template.json

{
  "TEMPLATE": {
    "BODY": {
      "name": "talos",
      "deployment": "straight",
      "description": "TALOS",
      "roles": [
        {
          "name": "controlplane",
          "cardinality": 1,
          "vm_template": 21,
          "shutdown_action": "terminate-hard",
          "vm_template_contents": "NIC = [\n  NAME = \"_NIC0\",\n  NETWORK_ID = \"$controlplane\" ]\n ",
          "min_vms": 1,
          "max_vms": 3,
          "cooldown": 5
        },
        {
          "name": "worker",
          "cardinality": 1,
          "vm_template": 21,
          "shutdown_action": "terminate-hard",
          "parents": [
            "controlplane"
          ],
          "vm_template_contents": "NIC = [\n  NAME = \"_NIC1\",\n  NETWORK_ID = \"$worker\" ]\n ",
          "min_vms": 1,
          "max_vms": 5,
          "cooldown": 5
        }
      ],
      "networks": {
        "controlplane": "M|network|Controlplane Reserved| |reserve_from:1:SIZE=3",
        "worker": "M|network|Worker Reserved| |reserve_from:1:SIZE=5"
      }      
    }
  }
}

Expected behavior

The network reserve should be successfully initiated and created

Actual behavior

The specified network reserve creation step is not being executed. Instead, it appears to be skipped or not processed as intended, resulting in the network reserve not being created successfully.

Steps to Reproduce

Deploy OneFlow service by terraform oneflow show 116 --json

{
 "DOCUMENT": {
   "ID": "116",
   "UID": "0",
   "GID": "0",
   "UNAME": "oneadmin",
   "GNAME": "oneadmin",
   "NAME": "talos",
   "TYPE": "100",
   "PERMISSIONS": {
     "OWNER_U": "1",
     "OWNER_M": "1",
     "OWNER_A": "0",
     "GROUP_U": "0",
     "GROUP_M": "0",
     "GROUP_A": "0",
     "OTHER_U": "0",
     "OTHER_M": "0",
     "OTHER_A": "0"
   },
   "TEMPLATE": {
     "BODY": {
       "deployment": "straight",
       "description": "TALOS",
       "name": "talos",
       "networks": {
         "controlplane": "M|network|Controlplane Reserved| |reserve_from:1:SIZE=3",
         "worker": "M|network|Worker Reserved| |reserve_from:1:SIZE=5"
       },
       "roles": [
         {
           "cardinality": 1,
           "cooldown": 5,
           "max_vms": 3,
           "min_vms": 1,
           "name": "controlplane",
           "shutdown_action": "terminate-hard",
           "vm_template": 21,
           "vm_template_contents": "NIC = [\n  NAME = \"_NIC0\",\n  NETWORK_ID = \"\" ]\n ",
           "state": 2,
           "nodes": [
             {
               "deploy_id": 101,
               "vm_info": {
                 "VM": {
                   "ID": "101",
                   "UID": "0",
                   "GID": "0",
                   "UNAME": "oneadmin",
                   "GNAME": "oneadmin",
                   "NAME": "controlplane_0_(service_116)"
                 }
               }
             }
           ],
           "on_hold": false,
           "last_vmname": 1
         },
         {
           "cardinality": 1,
           "cooldown": 5,
           "max_vms": 5,
           "min_vms": 1,
           "name": "worker",
           "parents": [
             "controlplane"
           ],
           "shutdown_action": "terminate-hard",
           "vm_template": 21,
           "vm_template_contents": "NIC = [\n  NAME = \"_NIC1\",\n  NETWORK_ID = \"\" ]\n ",
           "state": 2,
           "nodes": [
             {
               "deploy_id": 102,
               "vm_info": {
                 "VM": {
                   "ID": "102",
                   "UID": "0",
                   "GID": "0",
                   "UNAME": "oneadmin",
                   "GNAME": "oneadmin",
                   "NAME": "worker_0_(service_116)"
                 }
               }
             }
           ],
           "on_hold": false,
           "last_vmname": 1
         }
       ],
       "registration_time": 1706921141,
       "custom_attrs_values": {
       },
       "networks_values": [
         {
           "controlplane": {
             "extra": "NAME=CONTROLPLANE\nSIZE=3",
             "reserve_from": "1",
             "id": null
           },
           "worker": {
             "extra": "NAME=WORKER\nSIZE=5",
             "reserve_from": "1",
             "id": null
           }
         }
       ],
       "state": 2,
       "start_time": 1706922196,
       "log": [
         {
           "timestamp": 1706922196,
           "severity": "I",
           "message": "New state: DEPLOYING_NETS"
         },
         {
           "timestamp": 1706922196,
           "severity": "I",
           "message": "New state: DEPLOYING"
         },
         {
           "timestamp": 1706922780,
           "severity": "I",
           "message": "New state: RUNNING"
         }
       ]
     }
   }
 }
}

NETWORK_ID = "" is empty Deploy OneFlow service by oneflow-template instantiate 95 talos-service.json

{
  "DOCUMENT": {
    "ID": "88",
    "UID": "0",
    "GID": "0",
    "UNAME": "oneadmin",
    "GNAME": "oneadmin",
    "NAME": "talos",
    "TYPE": "100",
    "PERMISSIONS": {
      "OWNER_U": "1",
      "OWNER_M": "1",
      "OWNER_A": "0",
      "GROUP_U": "0",
      "GROUP_M": "0",
      "GROUP_A": "0",
      "OTHER_U": "0",
      "OTHER_M": "0",
      "OTHER_A": "0"
    },
    "TEMPLATE": {
      "BODY": {
        "name": "talos",
        "deployment": "straight",
        "description": "TALOS",
        "roles": [
          {
            "name": "controlplane",
            "cardinality": 1,
            "vm_template": 21,
            "shutdown_action": "terminate-hard",
            "vm_template_contents": "NIC = [\n  NAME = \"_NIC0\",\n  NETWORK_ID = \"17\" ]\n",
            "min_vms": 1,
            "max_vms": 3,
            "cooldown": 5,
            "elasticity_policies": [

            ],
            "scheduled_policies": [

            ],
            "user_inputs_values": {
            },
            "state": 2,
            "nodes": [
              {
                "deploy_id": 70,
                "vm_info": {
                  "VM": {
                    "ID": "70",
                    "UID": "1",
                    "GID": "0",
                    "UNAME": "serveradmin",
                    "GNAME": "oneadmin",
                    "NAME": "controlplane_0_(service_88)"
                  }
                }
              }
            ],
            "on_hold": false,
            "last_vmname": 1
          },
          {
            "name": "worker",
            "cardinality": 1,
            "vm_template": 21,
            "shutdown_action": "terminate-hard",
            "parents": [
              "controlplane"
            ],
            "vm_template_contents": "NIC = [\n  NAME = \"_NIC0\",\n  NETWORK_ID = \"18\" ]\n",
            "min_vms": 1,
            "max_vms": 5,
            "cooldown": 5,
            "elasticity_policies": [

            ],
            "scheduled_policies": [

            ],
            "user_inputs_values": {
            },
            "state": 2,
            "nodes": [
              {
                "deploy_id": 71,
                "vm_info": {
                  "VM": {
                    "ID": "71",
                    "UID": "1",
                    "GID": "0",
                    "UNAME": "serveradmin",
                    "GNAME": "oneadmin",
                    "NAME": "worker_0_(service_88)"
                  }
                }
              }
            ],
            "on_hold": false,
            "last_vmname": 1
          }
        ],
        "ready_status_gate": false,
        "automatic_deletion": true,
        "networks": {
          "controlplane": "M|network|Controlplane Reserved| |reserve_from:1:SIZE=3",
          "worker": "M|network|Worker Reserved| |reserve_from:1:SIZE=5"
        },
        "shutdown_action": "terminate-hard",
        "registration_time": 1706913823,
        "custom_attrs_values": {
        },
        "networks_values": [
          {
            "controlplane": {
              "reserve_from": "1",
              "extra": "SIZE=3\nNAME=\"controlplane-88\"\n",
              "id": 17
            }
          },
          {
            "worker": {
              "reserve_from": "1",
              "extra": "SIZE=5\nNAME=\"worker-88\"\n",
              "id": 18
            }
          }
        ],
        "state": 2,
        "start_time": 1706914234,
        "log": [
          {
            "timestamp": 1706914234,
            "severity": "I",
            "message": "New state: DEPLOYING_NETS"
          },
          {
            "timestamp": 1706914271,
            "severity": "I",
            "message": "New state: DEPLOYING"
          },
          {
            "timestamp": 1706914303,
            "severity": "I",
            "message": "New state: RUNNING"
          }
        ]
      }
    }
  }
}

network is 17 and 18 created successfully

Debug output

No response

Panic output

No response

Important factoids

No response

References

No response

shurkus avatar Feb 03 '24 01:02 shurkus

Any news or need some additional information?

shurkus avatar Feb 12 '24 11:02 shurkus

An issue has been identified when using the goca library to create a service in OpenNebula. Code has been written to initialize service creation; however, it has been observed that networks, which should be created using reserve_from, are not being created.

Code Sample:

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"

	"github.com/OpenNebula/one/src/oca/go/src/goca"
)

func main() {
	// Get a Flow client
	client := goca.NewDefaultFlowClient(
		goca.NewFlowConfig("oneadmin", "oneadmin", "http://192.168.1.5:2474"),
	)

	// Get a Flow controller
	controller := goca.NewControllerFlow(client)

	// if template id is set, instantiate a Service from this template
	tc := controller.STemplate(172)

	// Read the content of extra_template.json
	extraTemplateContent, err := ioutil.ReadFile("extra_template.json")
	if err != nil {
		fmt.Println("Error reading extra_template.json:", err)
		return
	}

	// Decode the JSON content into a map[string]interface{}
	var data map[string]interface{}
	if err := json.Unmarshal(extraTemplateContent, &data); err != nil {
		fmt.Println("Error decoding JSON:", err)
		return
	}

	// Print the decoded JSON data for debugging
	fmt.Println("Decoded JSON data:", data)

	// Instantiate the service using the decoded JSON data
	service, err := tc.Instantiate(string(extraTemplateContent))
	if err != nil {
		fmt.Println("Error instantiating service:", err)
		return
	}

	// Print the instantiated service for debugging
	fmt.Println("Instantiated service:", service)
}

When running this code, it is expected that networks will be created according to the data passed in extra_template.json. However, it has been observed that networks are not being created, and it is suspected that the issue may be related to the goca library or some deeper level within OpenNebula.

shurkus avatar Feb 27 '24 14:02 shurkus

Understood. The issue lies within OpenNebula. Golang operates with maps that don't maintain order, resulting in JSON output sorted alphabetically. Consequently, a structure like:

"Controlplane": {
   "reserve_from": "1",
   "extra": "SIZE=3"
}

can be reordered to:

"Controlplane": {
   "extra": "SIZE=3",
   "reserve_from": "1"
}

However, OpenNebula has hardcoded logic (e.g., here) that expects specific first keys like 'id', 'template_id', or 'reserve_from', but encounters unexpected ones like 'extra' and consequently skips the processing step, which is incorrect.

A potential solution could look like this:

def deploy_networks(deploy = true)
  # Unpack template body if provided as JSON string
  template_body = deploy ? JSON.parse(self['TEMPLATE/BODY']) : @body

  # Return if no network information found in the template body
  return if template_body['networks_values'].nil?

  # Process each network in the networks_values list
  template_body['networks_values'].each do |network|
    network.each do |name, properties|
      # Skip iteration if the key is 'id'
      next if properties.key?('id')

      # Create or reserve the network based on the presence of 'template_id' or 'reserve_from' keys
      case
      when properties.key?('template_id')
        rc = create_vnet(name, properties)
      when properties.key?('reserve_from')
        rc = reserve(name, properties)
      end

      # Return error if something went wrong during network creation or reservation
      return rc if OpenNebula.is_error?(rc)

      # Update the 'id' value in the current network
      properties['id'] = rc
    end
  end if deploy

  # Replace variables in the template with corresponding values
  resolve_networks(template_body)

  # Update the template body
  update_body(template_body)
end

However, the hardcoded logic continues in delete_networks and networks functions (maybe somewhere else).

shurkus avatar Feb 27 '24 23:02 shurkus

Hi @shurkus, thank you very much for bringing this problem to our attention! We have been analyzing this case and we think you are absolutely right. As you point out, this bug comes from OpenNebula (specifically from Flow server), so it will be fixed in the next OpenNebula release.

vickmp avatar Feb 28 '24 10:02 vickmp

Apologies for creating the pull request from my other account. Additionally, I would like to bring your attention to packer-plugin-opennebula. Unfortunately, I don't have the time to continue its development, so it would be great if you could consider taking it over and providing further support.

shurkus avatar Feb 28 '24 22:02 shurkus