Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with Services: Unable to Create Reserve from Network #527

Closed
shurkus opened this issue Feb 3, 2024 · 6 comments
Closed

Issue with Services: Unable to Create Reserve from Network #527

shurkus opened this issue Feb 3, 2024 · 6 comments

Comments

@shurkus
Copy link

shurkus commented Feb 3, 2024

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
Copy link
Author

shurkus commented Feb 12, 2024

Any news or need some additional information?

@shurkus
Copy link
Author

shurkus commented Feb 27, 2024

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
Copy link
Author

shurkus commented Feb 27, 2024

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).

@vickmp
Copy link
Member

vickmp commented Feb 28, 2024

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.

@shurkus
Copy link
Author

shurkus commented Feb 28, 2024

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.

@rsmontero
Copy link
Member

rsmontero commented May 10, 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants