terraform-provider-opennebula
terraform-provider-opennebula copied to clipboard
Issue with Services: Unable to Create Reserve from Network
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
Any news or need some additional information?
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.
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).
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.
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.