parent
8058b1c08f
commit
e501417166
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||||
|
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package decort
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
// "hash/fnv"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
// "net/url"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
|
||||||
|
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func flattenPfw(d *schema.ResourceData, pfwFacts string) error {
|
||||||
|
// NOTE: this function modifies ResourceData argument - as such it should never be called
|
||||||
|
// from resourcePfwExists(...) method
|
||||||
|
// log.Debugf("flattenPfw: ready to decode response body from API %s", pfwFacts)
|
||||||
|
pfwRecord := ComputePfwListResp{}
|
||||||
|
err := json.Unmarshal([]byte(pfwFacts), &pfwRecord)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("flattenPfw: decoded %d PFW rules for compute ID %s on ViNS ID %d",
|
||||||
|
len(pfwRecord.Rules), pfwRecord.Header.VinsID, pfwRecord.Header.VinsID)
|
||||||
|
|
||||||
|
/*
|
||||||
|
combo := fmt.Sprintf("%d:%d", compId.(int), pfwRecord.ViNS.VinsID)
|
||||||
|
hasher := fnv.New32a()
|
||||||
|
hasher.Write([]byte(combo))
|
||||||
|
d.SetId(fmt.Sprintf("%d", hasher.Sum32()))
|
||||||
|
*/
|
||||||
|
// set ID of this PFW rule set as "compute_id:vins_id"
|
||||||
|
d.SetId(fmt.Sprintf("%d:%d", pfwRecord.Header.ComputeID, pfwRecord.Header.VinsID))
|
||||||
|
log.Debugf("flattenPfw: PFW rule set ID %s", d.Id())
|
||||||
|
d.Set("compute_id", pfwRecord.Header.ComputeID)
|
||||||
|
d.Set("vins_id", pfwRecord.Header.VinsID)
|
||||||
|
|
||||||
|
pfwRulesList := []interface{}{}
|
||||||
|
for _, runner := range pfwRecord.Rules {
|
||||||
|
rule := map[string]interface{}{
|
||||||
|
"pub_port_start": runner.PublicPortStart,
|
||||||
|
"pub_port_end": runner.PublicPortEnd,
|
||||||
|
"local_port": runner.LocalPort,
|
||||||
|
"proto": runner.Protocol,
|
||||||
|
"rule_id": runner.ID,
|
||||||
|
}
|
||||||
|
pfwRulesList = append(pfwRulesList, rule)
|
||||||
|
}
|
||||||
|
if err = d.Set("rule", pfwRulesList); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dataSourcePfwRead(d *schema.ResourceData, m interface{}) error {
|
||||||
|
pfwFacts, err := utilityPfwCheckPresence(d, m)
|
||||||
|
if pfwFacts == "" {
|
||||||
|
// if empty string is returned from dataSourcePfwRead then we got no
|
||||||
|
// PFW rules. It could also be because there was some error, which
|
||||||
|
// is indicated by non-nil err value
|
||||||
|
d.SetId("") // ensure ID is empty in this case anyway
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return flattenPfw(d, pfwFacts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dataSourcePfw() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
SchemaVersion: 1,
|
||||||
|
|
||||||
|
Read: dataSourcePfwRead,
|
||||||
|
|
||||||
|
Timeouts: &schema.ResourceTimeout{
|
||||||
|
Read: &Timeout30s,
|
||||||
|
Default: &Timeout60s,
|
||||||
|
},
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"compute_id": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
ValidateFunc: validation.IntAtLeast(1),
|
||||||
|
Description: "ID of the compute instance to configure port forwarding rules for.",
|
||||||
|
},
|
||||||
|
|
||||||
|
"vins_id": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
ValidateFunc: validation.IntAtLeast(1),
|
||||||
|
Description: "ID of the ViNS to configure port forwarding rules on. Compute must be already plugged into this ViNS and ViNS must have external network connection.",
|
||||||
|
},
|
||||||
|
|
||||||
|
"rule": {
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: rulesSubresourceSchemaMake(),
|
||||||
|
},
|
||||||
|
Description: "Port forwarding rule. You may specify several rules, one in each such block.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||||
|
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package decort
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
// "encoding/json"
|
||||||
|
"fmt"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
|
||||||
|
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourcePfwCreate(d *schema.ResourceData, m interface{}) error {
|
||||||
|
compId := d.Get("compute_id")
|
||||||
|
|
||||||
|
rules_set, ok := d.GetOk("rules")
|
||||||
|
if !ok || rules_set.(*schema.Set).Len() == 0 {
|
||||||
|
log.Debugf("resourcePfwCreate: empty new PFW rules set requested for compute ID %d - nothing to create", compId.(int))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("resourcePfwCreate: ready to setup %d PFW rules for compute ID %d",
|
||||||
|
rules_set.(*schema.Set).Len(), compId.(int))
|
||||||
|
|
||||||
|
controller := m.(*ControllerCfg)
|
||||||
|
apiErrCount := 0
|
||||||
|
var lastSavedError error
|
||||||
|
|
||||||
|
for _, runner := range rules_set.(*schema.Set).List() {
|
||||||
|
rule := runner.(map[string]interface{})
|
||||||
|
params := &url.Values{}
|
||||||
|
params.Add("computeId", fmt.Sprintf("%d", compId.(int)))
|
||||||
|
params.Add("publicPortStart", fmt.Sprintf("%d", rule["pub_port_start"].(int)))
|
||||||
|
params.Add("publicPortEnd", fmt.Sprintf("%d", rule["pub_port_end"].(int)))
|
||||||
|
params.Add("localBasePort", fmt.Sprintf("%d", rule["local_port"].(int)))
|
||||||
|
params.Add("proto", rule["proto"].(string))
|
||||||
|
log.Debugf("resourcePfwCreate: ready to add rule %d:%d -> %d %s for Compute ID %d",
|
||||||
|
rule["pub_port_start"].(int),rule["pub_port_end"].(int),
|
||||||
|
rule["local_port"].(int), rule["proto"].(string),
|
||||||
|
compId.(int))
|
||||||
|
_, err, _ := controller.decortAPICall("POST", ComputePfwAddAPI, params)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("resourcePfwCreate: error adding rule %d:%d -> %d %s for Compute ID %d: %s",
|
||||||
|
rule["pub_port_start"].(int),rule["pub_port_end"].(int),
|
||||||
|
rule["local_port"].(int), rule["proto"].(string),
|
||||||
|
compId.(int),
|
||||||
|
err)
|
||||||
|
apiErrCount++
|
||||||
|
lastSavedError = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if apiErrCount > 0 {
|
||||||
|
log.Errorf("resourcePfwCreate: there were %d error(s) adding PFW rules to Compute ID %s. Last error was: %s",
|
||||||
|
apiErrCount, compId.(int), lastSavedError)
|
||||||
|
return lastSavedError
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePfwRead(d *schema.ResourceData, m interface{}) error {
|
||||||
|
pfwFacts, err := utilityPfwCheckPresence(d, m)
|
||||||
|
if pfwFacts == "" {
|
||||||
|
// if empty string is returned from dataSourcePfwRead then we got no
|
||||||
|
// PFW rules. It could also be because there was some error, which
|
||||||
|
// is indicated by non-nil err value
|
||||||
|
d.SetId("") // ensure ID is empty in this case anyway
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return flattenPfw(d, pfwFacts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePfwUpdate(d *schema.ResourceData, m interface{}) error {
|
||||||
|
// TODO: update not implemented yet
|
||||||
|
compId := d.Get("compute_id")
|
||||||
|
return fmt.Errorf("resourcePfwUpdate: method is not implemented yet (Compute ID %d)", compId.(int))
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePfwDelete(d *schema.ResourceData, m interface{}) error {
|
||||||
|
compId := d.Get("compute_id")
|
||||||
|
|
||||||
|
rules_set, ok := d.GetOk("rules")
|
||||||
|
if !ok || rules_set.(*schema.Set).Len() == 0 {
|
||||||
|
log.Debugf("resourcePfwCreate: no PFW rules defined for compute ID %d - nothing to delete", compId.(int))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("resourcePfwDelete: ready to delete %d PFW rules from compute ID %d",
|
||||||
|
rules_set.(*schema.Set).Len(), compId.(int))
|
||||||
|
|
||||||
|
controller := m.(*ControllerCfg)
|
||||||
|
apiErrCount := 0
|
||||||
|
var lastSavedError error
|
||||||
|
|
||||||
|
for _, runner := range rules_set.(*schema.Set).List() {
|
||||||
|
rule := runner.(map[string]interface{})
|
||||||
|
params := &url.Values{}
|
||||||
|
params.Add("computeId", fmt.Sprintf("%d", compId.(int)))
|
||||||
|
params.Add("ruleId", fmt.Sprintf("%d", rule["id"].(int)))
|
||||||
|
log.Debugf("resourcePfwCreate: ready to delete rule ID%s (%d:%d -> %d %s) from Compute ID %d",
|
||||||
|
rule["id"].(int),
|
||||||
|
rule["pub_port_start"].(int),rule["pub_port_end"].(int),
|
||||||
|
rule["local_port"].(int), rule["proto"].(string),
|
||||||
|
compId.(int))
|
||||||
|
_, err, _ := controller.decortAPICall("POST", ComputePfwDelAPI, params)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("resourcePfwDelete: error deleting rule ID %d (%d:%d -> %d %s) from Compute ID %d: %s",
|
||||||
|
rule["id"].(int),
|
||||||
|
rule["pub_port_start"].(int),rule["pub_port_end"].(int),
|
||||||
|
rule["local_port"].(int), rule["proto"].(string),
|
||||||
|
compId.(int),
|
||||||
|
err)
|
||||||
|
apiErrCount++
|
||||||
|
lastSavedError = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if apiErrCount > 0 {
|
||||||
|
log.Errorf("resourcePfwDelete: there were %d error(s) when deleting PFW rules from Compute ID %s. Last error was: %s",
|
||||||
|
apiErrCount, compId.(int), lastSavedError)
|
||||||
|
return lastSavedError
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePfwExists(d *schema.ResourceData, m interface{}) (bool, error) {
|
||||||
|
// Reminder: according to Terraform rules, this function should not modify its ResourceData argument
|
||||||
|
log.Debugf("resourcePfwExists: called for Compute ID %d", d.Get("compute_id").(int))
|
||||||
|
|
||||||
|
pfwFacts, err := utilityPfwCheckPresence(d, m)
|
||||||
|
if pfwFacts == "" {
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePfw() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
SchemaVersion: 1,
|
||||||
|
|
||||||
|
Create: resourcePfwCreate,
|
||||||
|
Read: resourcePfwRead,
|
||||||
|
Update: resourcePfwUpdate,
|
||||||
|
Delete: resourcePfwDelete,
|
||||||
|
Exists: resourcePfwExists,
|
||||||
|
|
||||||
|
Importer: &schema.ResourceImporter{
|
||||||
|
State: schema.ImportStatePassthrough,
|
||||||
|
},
|
||||||
|
|
||||||
|
Timeouts: &schema.ResourceTimeout{
|
||||||
|
Create: &Timeout180s,
|
||||||
|
Read: &Timeout30s,
|
||||||
|
Update: &Timeout180s,
|
||||||
|
Delete: &Timeout60s,
|
||||||
|
Default: &Timeout60s,
|
||||||
|
},
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"compute_id": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
ValidateFunc: validation.IntAtLeast(1),
|
||||||
|
Description: "ID of the compute instance to configure port forwarding rules for.",
|
||||||
|
},
|
||||||
|
|
||||||
|
"vins_id": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
ValidateFunc: validation.IntAtLeast(1),
|
||||||
|
Description: "ID of the ViNS to configure port forwarding rules on. Compute must be already plugged into this ViNS and ViNS must have external network connection.",
|
||||||
|
},
|
||||||
|
|
||||||
|
"rule": {
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: rulesSubresourceSchemaMake(),
|
||||||
|
},
|
||||||
|
Description: "Port forwarding rule. You may specify several rules, one in each such block.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2019-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||||
|
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package decort
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
// "encoding/json"
|
||||||
|
// "fmt"
|
||||||
|
// "bytes"
|
||||||
|
// log "github.com/sirupsen/logrus"
|
||||||
|
// "net/url"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
|
||||||
|
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is rules subresource of PFW resource used
|
||||||
|
// when creating/managing port forwarding rules for a compute connected
|
||||||
|
// to the corresponding network
|
||||||
|
|
||||||
|
func rulesSubresourceSchemaMake() map[string]*schema.Schema {
|
||||||
|
rets := map[string]*schema.Schema{
|
||||||
|
"pub_port_start": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
ValidateFunc: validation.IntBetween(1, 65535),
|
||||||
|
Description: "Port number on the external interface. For a ranged rule it set the starting port number.",
|
||||||
|
},
|
||||||
|
|
||||||
|
"pub_port_end": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
ValidateFunc: validation.IntBetween(1, 65535),
|
||||||
|
Description: "End port number on the external interface for a ranged rule. Set it equal to start port for a single port rule.",
|
||||||
|
},
|
||||||
|
|
||||||
|
"local_port": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
ValidateFunc: validation.IntBetween(1, 65535),
|
||||||
|
Description: "Port number on the local interface.",
|
||||||
|
},
|
||||||
|
|
||||||
|
"proto": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
StateFunc: stateFuncToLower,
|
||||||
|
ValidateFunc: validation.StringInSlice([]string{"tcp", "udp"}, false),
|
||||||
|
Description: "Protocol for this rule. Could be either tcp or udp.",
|
||||||
|
},
|
||||||
|
|
||||||
|
// the rest are computed
|
||||||
|
|
||||||
|
"rule_id": {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Computed: true,
|
||||||
|
Description: "Rule ID as assigned by the cloud platform.",
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
return rets
|
||||||
|
}
|
@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2020-2021 Digital Energy Cloud Solutions LLC. All Rights Reserved.
|
||||||
|
Author: Sergey Shubin, <sergey.shubin@digitalenergy.online>, <svs1370@gmail.com>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration
|
||||||
|
Technology platfom.
|
||||||
|
|
||||||
|
Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package decort
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func utilityPfwCheckPresence(d *schema.ResourceData, m interface{}) (string, error) {
|
||||||
|
//
|
||||||
|
// This function does not modify its ResourceData argument, so it is safe to use it as core
|
||||||
|
// method for the Terraform resource Exists method.
|
||||||
|
//
|
||||||
|
|
||||||
|
controller := m.(*ControllerCfg)
|
||||||
|
urlValues := &url.Values{}
|
||||||
|
|
||||||
|
// NOTE on importing PFW into TF state resource:
|
||||||
|
//
|
||||||
|
// Port forward rules are NOT represented by any "individual" resource in the platform.
|
||||||
|
// Consequently, there is no unique ID reported by the platform that could be used to
|
||||||
|
// identify PFW rule set.
|
||||||
|
// However, we need some ID to identify PFW resource in TF state, and compute ID is the most
|
||||||
|
// convenient way, as it is:
|
||||||
|
// 1) unique;
|
||||||
|
// 2) compute may have only one PFW rule set.
|
||||||
|
//
|
||||||
|
|
||||||
|
var compId, vinsId int
|
||||||
|
|
||||||
|
if d.Id() != "" {
|
||||||
|
log.Debugf("utilityPfwCheckPresence: setting context from d.Id() %s", d.Id())
|
||||||
|
idParts := strings.SplitN(d.Id(), ":", 2)
|
||||||
|
compId, _ = strconv.Atoi(idParts[0])
|
||||||
|
vinsId, _ = strconv.Atoi(idParts[1])
|
||||||
|
log.Debugf("utilityPfwCheckPresence: extracted Compute ID %d, ViNS %d", compId, vinsId)
|
||||||
|
if compId <= 0 || vinsId <= 0 {
|
||||||
|
return "", fmt.Errorf("Ivalid context from d.Id %s", d.Id())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scId, cSet := d.GetOk("compute_id")
|
||||||
|
svId, vSet := d.GetOk("vins_id")
|
||||||
|
if cSet || vSet {
|
||||||
|
log.Debugf("utilityPfwCheckPresence: setting Compute ID from schema")
|
||||||
|
compId = scId.(int)
|
||||||
|
vinsId = svId.(int)
|
||||||
|
log.Debugf("utilityPfwCheckPresence: extractted Compute ID %d, ViNS %d", compId, vinsId)
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("Cannot get context to check PFW rules neither from d.Id() nor from schema")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("utilityPfwCheckPresence: preparing to get PFW rules for Compute ID %d on ViNS ID %d", compId, vinsId)
|
||||||
|
|
||||||
|
urlValues.Add("computeId", fmt.Sprintf("%d", compId))
|
||||||
|
apiResp, err, respCode := controller.decortAPICall("POST", ComputePfwListAPI, urlValues)
|
||||||
|
if respCode == 500 {
|
||||||
|
// this is workaround for API 3.7.0 "feature" - will be removed in one of the future versions
|
||||||
|
log.Errorf("utilityPfwCheckPresence: Compute ID %d has no PFW and no connection to PFW-ready ViNS", compId)
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
pfwListResp := ComputePfwListResp{}
|
||||||
|
|
||||||
|
// Note the specifics of compute/pfwList response in API 3.7.x (this may be changed in the future):
|
||||||
|
// 1) if there are no PFW rules and compute is not connected to any PFW-able ViNS
|
||||||
|
// the response will be empty string (or HTTP error code 500)
|
||||||
|
// 2) if there are no PFW rules but compute is connected to a PFW-able ViNS
|
||||||
|
// the response will contain a list with a single element - prefix (see PfwPrefixRecord)
|
||||||
|
// 3) if there are port forwarding rules, the response will contain a list which starts
|
||||||
|
// with prefix (see PfwPrefixRecord) and then followed by one or more rule records
|
||||||
|
// (see PfwRuleRecord)
|
||||||
|
//
|
||||||
|
// EXTRA NOTE: in API 3.7.0 and the likes pfwList returns HTTP response code 500 for a compute
|
||||||
|
// that is not connected to any PFW-able ViNS - need to implement temporary workaround
|
||||||
|
|
||||||
|
if apiResp == "" {
|
||||||
|
// No port forward rules defined for this compute
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("utilityPfwCheckPresence: ready to split API response string %s", apiResp)
|
||||||
|
|
||||||
|
twoParts := strings.SplitN(apiResp, "},", 2)
|
||||||
|
if len(twoParts) < 1 || len(twoParts) > 2 {
|
||||||
|
// Case: invalid format of API response
|
||||||
|
log.Errorf("utilityPfwCheckPresence: non-empty pfwList response for compute ID %d failed to split properly", compId)
|
||||||
|
return "", fmt.Errorf("Non-empty pfwList response failed to split properly")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(twoParts) == 1 {
|
||||||
|
// Case: compute is connected to a PWF-ready ViNS but has no PFW rules defined
|
||||||
|
log.Debugf("utilityPfwCheckPresence: compute ID %d is connected to PFW-ready ViNS but has no PFW rules", compId)
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case: compute is connected to a PFW ready ViNS and has some PFW rule
|
||||||
|
prefixResp := strings.TrimSuffix(strings.TrimPrefix(twoParts[0], "["), ",") + "}"
|
||||||
|
log.Debugf("utilityPfwCheckPresence: ready to unmarshal prefix part %s", prefixResp)
|
||||||
|
err = json.Unmarshal([]byte(prefixResp), &pfwListResp.Header)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("utilityPfwCheckPresence: failed to unmarshal prefix part of API response: %s", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
rulesResp := "[" + twoParts[1]
|
||||||
|
log.Debugf("utilityPfwCheckPresence: ready to unmarshal rules part %s", rulesResp)
|
||||||
|
err = json.Unmarshal([]byte(rulesResp), &pfwListResp.Rules)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("utilityPfwCheckPresence: failed to unmarshal rules part of API response: %s", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("utilityPfwCheckPresence: successfully read %d port forward rules for Compute ID %d, ViNS ID %d",
|
||||||
|
len(pfwListResp.Rules), compId, pfwListResp.Header.VinsID)
|
||||||
|
|
||||||
|
if pfwListResp.Header.VinsID != vinsId {
|
||||||
|
log.Errorf("utilityPfwCheckPresence: ViNS ID mismatch for PFW rules on compute ID %d: actual %d, required %d",
|
||||||
|
compId, pfwListResp.Header.VinsID, vinsId)
|
||||||
|
return "", fmt.Errorf("ViNS ID mismatch for PFW rules on compute ID %d: actual %d, required %d",
|
||||||
|
compId, pfwListResp.Header.VinsID, vinsId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reconstruct API response string for return
|
||||||
|
pfwListResp.Header.ComputeID = compId
|
||||||
|
reencodedItem, err := json.Marshal(pfwListResp)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(reencodedItem[:]), nil
|
||||||
|
}
|
Loading…
Reference in new issue