From d484da08b4fc1c499a13ef4861dabf12cc7a83ed Mon Sep 17 00:00:00 2001 From: Anna Khmelnitsky Date: Mon, 17 Apr 2023 23:39:42 +0000 Subject: [PATCH] Tool for detecting diff in NSX policy spec Tool that prints out new/deleted attributes in a predefined set of model objects from baseline NSX spec to another NSX version spec. In addition, adjust policy generation tool to recent SDK changes and make some further improvements. Signed-off-by: Anna Khmelnitsky --- tools/diffy/diffy.py | 122 +++++++++++++++++++++++ tools/resource_nsxt_policy_doc_template | 4 +- tools/resource_nsxt_policy_template | 60 ++++------- tools/resource_nsxt_policy_test_template | 8 -- 4 files changed, 143 insertions(+), 51 deletions(-) create mode 100755 tools/diffy/diffy.py diff --git a/tools/diffy/diffy.py b/tools/diffy/diffy.py new file mode 100755 index 000000000..4f44c266c --- /dev/null +++ b/tools/diffy/diffy.py @@ -0,0 +1,122 @@ +#!/usr/bin/python +# -*- coding: latin-1 -*- + +# Copyright (c) 2023 VMware, Inc. All Rights Reserved. +# SPDX-License-Identifier: MPL-2.0 + +# This script detects difference in model between different versions of NSX spec. List of relevant objects is for now hard-coded. + +import sys +import re +import os +import json + +OBJECTS = [ +"ContextProfile", +"DhcpRelayConfig", +"DhcpServerConfig", +"Domain", +"EvpnConfig", +"EvpnTenantConfig", +"GatewayPolicy", +"Group", +"IPSecVpnDpdProfile", +"IPSecVpnIkeProfile", +"IPSecVpnService", +"IPSecVpnTunnelProfile", +"IPSecVpnLocalEndpoint", +"IdsPolicy", +"IdsProfile", +"IpAddressBlock", +"IpAddressPool", +"L2VPNService", +"L2VPNSession", +"LBPool", +"LBService", +"LBVirtualServer", +"MacDiscoveryProfile", +"OspfConfig", +"PolicyDnsForwarder", +"PolicyNatRule", +"PrefixList", +"QoSProfile", +"SecurityPolicy", +"Segment", +"Service", +"StaticRoutes", +"Tier0", +"Tier0Interface", +"Tier1", +"Tier1Interface"] + +def load_spec(path): + + with open(path, 'r') as f: + spec = json.load(f) + + obj_map = {} + defs = spec["definitions"] + for key in defs: + if "allOf" not in defs[key]: + if "properties" in defs[key]: + # no inheritance + obj_map[key] = defs[key]["properties"] + continue + if len(defs[key]["allOf"]) == 2: + # object inheritance + if "properties" not in defs[key]["allOf"][1]: + continue + obj_map[key] = defs[key]["allOf"][1]["properties"] + + return obj_map + +def ref_to_def(ref): + return ref[len("#definitions/ "):] + +def print_ident(text, level): + ident = " "*level + print("%s%s" % (ident, text)) + +class color: + PURPLE = '\033[95m' + BLUE = '\033[94m' + END = '\033[0m' + +def main(): + + if len(sys.argv) < 3: + print("Usage: %s " % sys.argv[0]) + sys.exit() + + baseline_file_path = sys.argv[1] + target_file_path = sys.argv[2] + baseline_map = load_spec(baseline_file_path) + target_map = load_spec(target_file_path) + level = 0 + + def analyze_obj(obj, level): + print_ident("analyzing %s.." % obj, level) + if obj not in baseline_map: + return + for attr in target_map[obj]: + if attr not in baseline_map[obj]: + print_ident(color.BLUE + "new attribute %s" % attr + color.END, level + 1) + + if "$ref" in target_map[obj][attr]: + analyze_obj(ref_to_def(target_map[obj][attr]["$ref"]), level + 1) + if "items" in target_map[obj][attr] and "$ref" in target_map[obj][attr]["items"]: + analyze_obj(ref_to_def(target_map[obj][attr]["items"]["$ref"]), level + 1) + + for attr in baseline_map[obj]: + if obj not in target_map: + continue + if attr not in target_map[obj]: + print_ident(color.PURPLE + "deleted attribute %s" % attr + color.END, level + 1) + + for obj in OBJECTS: + analyze_obj(obj, level + 1) + + + print("Done.") + +main() diff --git a/tools/resource_nsxt_policy_doc_template b/tools/resource_nsxt_policy_doc_template index e8ba5d0b8..dad2fe563 100644 --- a/tools/resource_nsxt_policy_doc_template +++ b/tools/resource_nsxt_policy_doc_template @@ -43,10 +43,10 @@ In addition to arguments listed above, the following attributes are exported: An existing object can be [imported][docs-import] into this resource, via the following command: -[docs-import]: /docs/import/index.html +[docs-import]: https://www.terraform.io/cli/import ``` terraform import nsxt_policy_.test UUID ``` -The above command imports named `test` with the NSX ID `UUID`. +The above command imports named `test` with the NSX ID `UUID`. diff --git a/tools/resource_nsxt_policy_template b/tools/resource_nsxt_policy_template index 23c3f2b8b..e0a2b98d4 100644 --- a/tools/resource_nsxt_policy_template +++ b/tools/resource_nsxt_policy_template @@ -40,7 +40,7 @@ func resourceNsxtPolicy() *schema.Resource { } } -func resourceNsxtPolicyExists(id string, connector *client.RestConnector, isGlobalManager bool) (bool, error) { +func resourceNsxtPolicyExists(id string, connector client.Connector, isGlobalManager bool) (bool, error) { var err error if isGlobalManager { client := gm_infra.NewsClient(connector) @@ -60,15 +60,9 @@ func resourceNsxtPolicyExists(id string, connector *client.RestConne return false, logAPIError("Error retrieving resource", err) } -func resourceNsxtPolicyCreate(d *schema.ResourceData, m interface{}) error { +func resourceNsxtPolicyPatch(d *schema.ResourceData, m interface{}, id string) error { connector := getPolicyConnector(m) - // Initialize resource Id and verify this ID is not yet used - id, err := getOrGenerateID(d, m, resourceNsxtPolicyExists) - if err != nil { - return err - } - displayName := d.Get("display_name").(string) description := d.Get("description").(string) tags := getPolicyTagsFromSchema(d) @@ -81,19 +75,29 @@ func resourceNsxtPolicyCreate(d *schema.ResourceData, m interface{}) } - // Create the resource using PATCH - log.Printf("[INFO] Creating with ID %s", id) + log.Printf("[INFO] Patching with ID %s", id) if isPolicyGlobalManager(m) { gmObj, convErr := convertModelBindingType(obj, model.BindingType(), gm_model.BindingType()) if convErr != nil { return convErr } client := gm_infra.NewsClient(connector) - err = client.Patch(id, gmObj.(gm_model.)) - } else { - client := infra.NewsClient(connector) - err = client.Patch(id, obj) + return client.Patch(id, gmObj.(gm_model.)) } + + client := infra.NewsClient(connector) + return client.Patch(id, obj) +} + +func resourceNsxtPolicyCreate(d *schema.ResourceData, m interface{}) error { + + // Initialize resource Id and verify this ID is not yet used + id, err := getOrGenerateID(d, m, resourceNsxtPolicyExists) + if err != nil { + return err + } + + err = resourceNsxtPolicyPatch(d, m, id) if err != nil { return handleCreateError("", id, err) } @@ -147,39 +151,13 @@ func resourceNsxtPolicyRead(d *schema.ResourceData, m interface{}) e } func resourceNsxtPolicyUpdate(d *schema.ResourceData, m interface{}) error { - connector := getPolicyConnector(m) id := d.Id() if id == "" { return fmt.Errorf("Error obtaining ID") } - // Read the rest of the configured parameters - description := d.Get("description").(string) - displayName := d.Get("display_name").(string) - tags := getPolicyTagsFromSchema(d) - - - - obj := model.{ - DisplayName: &displayName, - Description: &description, - Tags: tags, - - } - - var err error - if isPolicyGlobalManager(m) { - gmObj, convErr := convertModelBindingType(obj, model.BindingType(), gm_model.BindingType()) - if convErr != nil { - return convErr - } - client := gm_infra.NewsClient(connector) - _, err = client.Update(id, gmObj.(gm_model.)) - } else { - client := infra.NewsClient(connector) - _, err = client.Update(id, obj) - } + err := resourceNsxtPolicyPatch(d, m, id) if err != nil { return handleUpdateError("", id, err) } diff --git a/tools/resource_nsxt_policy_test_template b/tools/resource_nsxt_policy_test_template index d6978db7d..02275c59f 100644 --- a/tools/resource_nsxt_policy_test_template +++ b/tools/resource_nsxt_policy_test_template @@ -162,10 +162,6 @@ resource "nsxt_policy_" "test" { scope = "scope1" tag = "tag1" } -} - -data "nsxt_policy_realization_info" "realization_info" { - path = nsxt_policy_.test.path }`, attrMap["display_name"], attrMap["description"]) } @@ -174,9 +170,5 @@ func testAccNsxtPolicyMinimalistic() string { resource "nsxt_policy_" "test" { display_name = "%s" -} - -data "nsxt_policy_realization_info" "realization_info" { - path = nsxt_policy_.test.path }`, accTestPolicyUpdateAttributes["display_name"]) }