diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ca8d346e..9c98a3b2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,27 @@ # Release Notes +## Version 0.12.0 +**Release Date**: 7th October 2020 +### Features +1. Enhanced feedback for multi region query. +2. Cache Regions +3. Cache Compartments for specific Profile. +4. Add functionality for Customer Premise Equipment. +5. IPSec Connections added. +6. Remote peering functionality added but current the user will still need to add the peering id. +7. Dynamic Routing Gateway simplified and connections created from the new IPSec Connection & Remote Peering Connection. +8. Database System/Autonomous name displayed on the canvas (ENH REQ: Issue: #120). +9. Experimental Import from Terraform JSON Format file. +10. Export to Resource Manage local GitHub directory. + +### Bug Fixes +1. Fix Service Gateway only offers all services when drawn but then provisions Object Storage #107 +2. Fix Service Gateway related route rules do not allow setting a Destination Service #109 +3. Add clean functionality to the json object to remove null / undefined to resolve null element issue in Autonomous Databases. +4. Update Virtual Cloud Network and Subnet CIDR generation to check existing CIDRs. This resolves the duplicate CIDR issue. + + ## Version 0.11.0 **Release Date**: 16th September 2020 ### Features diff --git a/README.md b/README.md index 1297b1d5b..6374437f2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# OCI Designer Toolkit [0.11.0](CHANGELOG.md#version-0.11.0) +# OCI Designer Toolkit [0.12.0](CHANGELOG.md#version-0.12.0) OCI designer toolKIT (OKIT) is a browser based tool that allows the user to [design](https://www.ateam-oracle.com/introduction-to-okit-the-oci-designer-toolkit), [deploy](https://www.ateam-oracle.com/introduction-to-okit-the-oci-designer-toolkit) and visualise ([introspect/query](https://www.ateam-oracle.com/the-oci-designer-toolkit-query-feature)) diff --git a/containers/docker/Dockerfile b/containers/docker/Dockerfile index 2f79985f4..d0f922a69 100644 --- a/containers/docker/Dockerfile +++ b/containers/docker/Dockerfile @@ -5,7 +5,7 @@ FROM oraclelinux:7-slim LABEL "provider"="Oracle" \ "issues"="https://github.com/oracle/oci-designer-toolkit/issues" \ - "version"="0.11.0" \ + "version"="0.12.0" \ "description"="OKIT Web Server Container." \ "copyright"="Copyright (c) 2020, Oracle and/or its affiliates." SHELL ["/bin/bash", "-c"] diff --git a/documentation/Installation.md b/documentation/Installation.md index bf4e7fb9c..746201f91 100644 --- a/documentation/Installation.md +++ b/documentation/Installation.md @@ -22,7 +22,7 @@ python modules are installed and in addition provide a simple flask server that ## Clone Repository Before the building either the Docker or Vagrant Images the project will need to be cloned, or downloaded, from the GitHub Repository (or downloaded). The master branch is always the latest Stable Release but previous releases can be found using -associated Release tag is in the format vX.Y.Z hence for the version 0.11.0 the Release tag will be **v0.11.0**. +associated Release tag is in the format vX.Y.Z hence for the version 0.12.0 the Release tag will be **v0.12.0**. The command shows how this can be cloned to the local machine. diff --git a/documentation/Usage.md b/documentation/Usage.md index a0140fe1b..dbc511da2 100644 --- a/documentation/Usage.md +++ b/documentation/Usage.md @@ -109,7 +109,7 @@ The hamburger menu in the top left will display a slide out menu with all availa - Service Gateway - Local Peering Gateway - Networking - - Fast Connect + - Fast Connect - Load Balancer - Network Security Group - Route Table diff --git a/okitweb/okitImport.py b/okitweb/okitImport.py index 83ab1d3ce..b2ac3daf3 100644 --- a/okitweb/okitImport.py +++ b/okitweb/okitImport.py @@ -35,8 +35,8 @@ def parseHclJson(): query_json = json.loads(parsed_query_string) logJson(query_json) # Import HCL - parser = OkitHclJsonParser(query_json) - response_json = parser.parse() + parser = OkitHclJsonParser() + response_json = parser.parse(query_json) logJson(response_json) return json.dumps(response_json, sort_keys=False, indent=2, separators=(',', ': ')) else: diff --git a/okitweb/okitOci.py b/okitweb/okitOci.py index 398f8c9a7..4c06066bd 100644 --- a/okitweb/okitOci.py +++ b/okitweb/okitOci.py @@ -30,26 +30,32 @@ from facades.ociBlockStorageVolumes import OCIBlockStorageVolumes from facades.ociCompartment import OCICompartments from facades.ociContainer import OCIContainers +from facades.ociCpeDeviceShapes import OCICpeDeviceShapes +from facades.ociCustomerPremiseEquipment import OCICustomerPremiseEquipments from facades.ociDatabaseSystem import OCIDatabaseSystems from facades.ociDatabaseSystemShape import OCIDatabaseSystemShapes from facades.ociDatabaseVersion import OCIDatabaseVersions from facades.ociDynamicRoutingGateway import OCIDynamicRoutingGateways from facades.ociFastConnect import OCIFastConnects +from facades.ociFastConnectProviderServices import OCIFastConnectProviderServices from facades.ociFileStorageSystems import OCIFileStorageSystems from facades.ociImage import OCIImages from facades.ociInstance import OCIInstances from facades.ociInstancePool import OCIInstancePools from facades.ociInternetGateway import OCIInternetGateways +from facades.ociIPSecConnection import OCIIPSecConnections from facades.ociLoadBalancer import OCILoadBalancers from facades.ociLocalPeeringGateway import OCILocalPeeringGateways from facades.ociNATGateway import OCINATGateways from facades.ociNetworkSecurityGroup import OCINetworkSecurityGroups from facades.ociObjectStorageBuckets import OCIObjectStorageBuckets from facades.ociRegion import OCIRegions +from facades.ociRemotePeeringConnection import OCIRemotePeeringConnections from facades.ociResourceManager import OCIResourceManagers from facades.ociRouteTable import OCIRouteTables from facades.ociSecurityList import OCISecurityLists from facades.ociServiceGateway import OCIServiceGateways +from facades.ociServices import OCIServices from facades.ociShape import OCIShapes from facades.ociSubnet import OCISubnets from facades.ociTenancy import OCITenancies @@ -210,6 +216,10 @@ def ociArtifacts(artifact): logger.info('---- Processing Compartments') oci_compartments = OCICompartments(config=config, profile=config_profile, compartment_id=query_json['compartment_id']) response_json = oci_compartments.list(filter=query_json.get('compartment_filter', None)) + elif artifact == 'CustomerPremiseEquipment': + logger.info('---- Processing Customer Premise Equipment') + oci_cpes = OCICustomerPremiseEquipments(config=config, profile=config_profile, compartment_id=query_json['compartment_id']) + response_json = oci_cpes.list(filter=query_json.get('cpe_filter', None)) elif artifact == 'DatabaseSystem': logger.info('---- Processing Database Systems') oci_database_systems = OCIDatabaseSystems(config=config, profile=config_profile, compartment_id=query_json['compartment_id']) @@ -238,6 +248,10 @@ def ociArtifacts(artifact): logger.info('---- Processing Internet Gateways') oci_internet_gateways = OCIInternetGateways(config=config, profile=config_profile, compartment_id=query_json['compartment_id'], vcn_id=query_json['vcn_id']) response_json = oci_internet_gateways.list(filter=query_json.get('internet_gateway_filter', None)) + elif artifact == 'IPSecConnection': + logger.info('---- Processing IPSec Connections') + oci_ipsec_connections = OCIIPSecConnections(config=config, profile=config_profile, compartment_id=query_json['compartment_id']) + response_json = oci_ipsec_connections.list(filter=query_json.get('ipsec_connection_filter', None)) elif artifact == 'LoadBalancer': logger.info('---- Processing Load Balancers') oci_load_balancers = OCILoadBalancers(config=config, profile=config_profile, compartment_id=query_json['compartment_id']) @@ -263,6 +277,10 @@ def ociArtifacts(artifact): logger.info('---- Processing OKE Clusters') oke_clusters = OCIContainers(config=config, profile=config_profile, compartment_id=query_json['compartment_id']) response_json = oke_clusters.list(filter=query_json.get('oke_cluster_filter', None)) + elif artifact == 'RemotePeeringConnection': + logger.info('---- Processing Remote Peering Connections') + oci_remote_peering_connections = OCIRemotePeeringConnections(config=config, profile=config_profile, compartment_id=query_json['compartment_id']) + response_json = oci_remote_peering_connections.list(filter=query_json.get('remote_peering_connection_filter', None)) elif artifact == 'RouteTable': logger.info('---- Processing Route Tables') oci_route_tables = OCIRouteTables(config=config, profile=config_profile, compartment_id=query_json['compartment_id'], vcn_id=query_json['vcn_id']) @@ -295,14 +313,31 @@ def ociArtifacts(artifact): def dropdownQuery(): if request.method == 'GET': dropdown_json = {} + # Regions + oci_regions = OCIRegions() + dropdown_json["regions"] = sorted(oci_regions.list(), key=lambda k: k['name']) + # Services + oci_services = OCIServices() + dropdown_json["services"] = sorted(oci_services.list(), key=lambda k: k['name']) + # Instance Shapes oci_shapes = OCIShapes() dropdown_json["shapes"] = sorted(oci_shapes.list(), key=lambda k: k['sort_key']) + # Instance Images + oci_images = OCIImages() + dropdown_json["images"] = sorted(oci_images.list(), key=lambda k: k['sort_key']) + # Database System Shapes db_system_shapes = OCIDatabaseSystemShapes() dropdown_json["db_system_shapes"] = sorted(db_system_shapes.list(), key=lambda k: k['shape']) + # Database Versions db_versions = OCIDatabaseVersions() dropdown_json["db_versions"] = sorted(db_versions.list(), key=lambda k: k['version']) - oci_images = OCIImages() - dropdown_json["images"] = sorted(oci_images.list(), key=lambda k: k['sort_key']) + # CPE Device Shapes + # TODO: Upgrade OCI Python Module + #cpe_device_shapes = OCICpeDeviceShapes() + #dropdown_json["cpe_device_shapes"] = sorted(cpe_device_shapes.list(), key=lambda k: k['cpe_device_info']['vendor']) + # Fast Connect Provider Services + fast_connect_provider_services = OCIFastConnectProviderServices() + dropdown_json["fast_connect_provider_services"] = sorted(fast_connect_provider_services.list(), key=lambda k: k['provider_name']) return dropdown_json else: return 'Unknown Method', 500 diff --git a/okitweb/okitWebDesigner.py b/okitweb/okitWebDesigner.py index 1a95d2867..8ade902d7 100644 --- a/okitweb/okitWebDesigner.py +++ b/okitweb/okitWebDesigner.py @@ -33,6 +33,7 @@ from generators.okitAnsibleGenerator import OCIAnsibleGenerator from generators.okitTerraform11Generator import OCITerraform11Generator from generators.okitTerraformGenerator import OCITerraformGenerator +from generators.okitResourceManagerGenerator import OCIResourceManagerGenerator # Configure logging logger = getLogger() @@ -224,6 +225,8 @@ def generate(language): generator = OCIAnsibleGenerator(template_root, destination_dir, request.json, use_vars=use_vars) elif language == 'terraform11': generator = OCITerraform11Generator(template_root, destination_dir, request.json) + elif language == 'resource-manager': + generator = OCIResourceManagerGenerator(template_root, destination_dir, request.json) generator.generate() generator.writeFiles() zipname = generator.createZipArchive(os.path.join(destination_dir, language), "/tmp/okit-{0:s}".format(str(language))) diff --git a/okitweb/static/okit/css/okit_console.css b/okitweb/static/okit/css/okit_console.css index 8c99a8304..38f1052d6 100644 --- a/okitweb/static/okit/css/okit_console.css +++ b/okitweb/static/okit/css/okit_console.css @@ -835,7 +835,7 @@ li a.parent-item:hover { height: 80px; width: 140px; margin: 50px auto; - position: relative + position: relative; } #misshapen-doughnut:before { @@ -861,6 +861,13 @@ li a.parent-item:hover { width: 110px } +#region_progress { + height: 80px; + width: 140px; + margin: 50px auto; + position: relative +} + /* ** Validation */ diff --git a/okitweb/static/okit/css/okit_designer.css b/okitweb/static/okit/css/okit_designer.css index 4f6523c57..c4a6ce47b 100644 --- a/okitweb/static/okit/css/okit_designer.css +++ b/okitweb/static/okit/css/okit_designer.css @@ -175,6 +175,29 @@ color: black; } +.okit-tab-progress { + animation: animate-icing 2.2s linear infinite; + background: linear-gradient(#eee, #eee) no-repeat; + position: relative; +} + +.okit-tab-progress:before { + background: linear-gradient(90deg, #a3f022, #3e9fff); + content: ""; + position: absolute; + height: 1.1em; + width: 87%; + z-index: -1 +} + +.okit-tab-progress:after { + background-color: #fff; + content: ""; + position: relative; + height: 1.1em; + width: 87%; +} + .okit-svg-canvas { display: block; padding: 0; @@ -186,7 +209,8 @@ .okit-canvas-details { display: block; - padding-bottom: 5px; + padding-top: 3px; + padding-bottom: 3px; width: 100%; overflow: hidden; } @@ -321,4 +345,8 @@ rect.highlight { rect.highlight-vnic { fill: #00cc00; -} \ No newline at end of file +} + +rect.highlight-association { + fill: #336600; +} diff --git a/okitweb/static/okit/js/okit.js b/okitweb/static/okit/js/okit.js index 75adddbc8..5771ed472 100644 --- a/okitweb/static/okit/js/okit.js +++ b/okitweb/static/okit/js/okit.js @@ -11,6 +11,22 @@ if (typeof JSON.clone !== "function") { return JSON.parse(JSON.stringify(obj)); }; } +/* +** Add Clean function to JSON to remove null & undefined elements + */ +if (typeof JSON.clean !== "function") { + JSON.clean = obj => { + if (Array.isArray(obj)) { + return obj + .map(v => (v && v instanceof Object) ? JSON.clean(v) : v) + .filter(v => !(v == null)); + } else { + return Object.entries(obj) + .map(([k, v]) => [k, v && v instanceof Object ? JSON.clean(v) : v]) + .reduce((a, [k, v]) => (v == null ? a : (a[k]=v, a)), {}); + } + } +} let selectedArtefact = null; @@ -44,6 +60,7 @@ class OkitOCIConfig { class OkitOCIData { constructor() { + this.compartments = []; this.load(); } @@ -58,7 +75,7 @@ class OkitOCIData { url: 'dropdown/data', dataType: 'text', contentType: 'application/json', - data: JSON.stringify(this), + data: JSON.stringify(this.cloneForSave()), success: function(resp) { console.info('OKIT Dropdown Data Saved'); }, @@ -69,6 +86,14 @@ class OkitOCIData { }); } + cloneForSave() { + let clone = JSON.clone(this); + if (developer_mode) { + clone.compartments = []; + } + return clone; + } + query() { let me = this; $.getJSON('oci/dropdown', function(resp) {$.extend(true, me, resp); me.save(); console.info(me);}); @@ -78,6 +103,10 @@ class OkitOCIData { ** Get functions to retrieve drop-down data. */ + getCpeDeviceShapes() { + return this.cpe_device_shapes; + } + getDBSystemShapes(family='') { if (family === '') { return this.db_system_shapes; @@ -141,6 +170,18 @@ class OkitOCIData { } return [...new Set(images)].sort((a, b) => b - a); } + + getRegions() { + return this.regions; + } + + getCompartments() { + return this.compartments; + } + + setCompartments(compartments) { + this.compartments = compartments; + } } class OkitSettings { @@ -157,6 +198,7 @@ class OkitSettings { this.last_used_region = ''; this.last_used_compartment = ''; this.hide_attached = true; + this.highlight_association = true; this.load(); } @@ -350,6 +392,24 @@ class OkitSettings { td.append('label') .attr('for', 'hide_attached') .text('Hide Attached Artefacts'); + // Highlight Associations + tr = tbody.append('div').attr('class', 'tr'); + tr.append('div').attr('class', 'td').text(''); + td = tr.append('div').attr('class', 'td'); + td.append('input') + .attr('id', 'highlight_association') + .attr('name', 'highlight_association') + .attr('type', 'checkbox') + .property('checked', this.highlight_association) + .on('change', function () { + if (autosave) { + me.highlight_association = $('#highlight_association').is(':checked'); + me.save(); + } + }); + td.append('label') + .attr('for', 'highlight_association') + .text('Highlight Associations'); /* // Config Profile tr = tbody.append('div').attr('class', 'tr'); diff --git a/okitweb/static/okit/js/okit_console.js b/okitweb/static/okit/js/okit_console.js index 6d306fbf0..9b3feffa4 100644 --- a/okitweb/static/okit/js/okit_console.js +++ b/okitweb/static/okit/js/okit_console.js @@ -4,8 +4,8 @@ */ console.info('Loaded Console Javascript'); -const okitVersion = '0.11.0'; -const okitReleaseDate = '16th September 2020'; +const okitVersion = '0.12.0'; +const okitReleaseDate = '7th October 2020'; // Validation const validate_error_colour = "#ff4d4d"; const validate_warning_colour = "#ffd633"; diff --git a/okitweb/static/okit/js/okit_designer.js b/okitweb/static/okit/js/okit_designer.js index b61316fc9..82cef07ca 100644 --- a/okitweb/static/okit/js/okit_designer.js +++ b/okitweb/static/okit/js/okit_designer.js @@ -339,6 +339,10 @@ function displayQueryDialog() { .attr('id', 'config_profile') .on('change', () => { console.info('Profile Select ' + $(jqId('config_profile')).val()); + okitSettings.profile = $(jqId('config_profile')).val(); + okitSettings.save(); + // Clear Existing Compartments + okitOciData.setCompartments([]); loadCompartments(); loadRegions(); }); @@ -419,6 +423,7 @@ function handleQueryOci(e) { okitSettings.home_region_key = ''; okitSettings.home_region = ''; ociRegions = []; + // Load Previous Profile $(jqId('config_profile')).val(okitSettings.profile); // Load Compartment Select loadCompartments(); @@ -429,37 +434,60 @@ function loadCompartments() { // Clear Select let select = $(jqId('query_compartment_id')); $(select).empty(); - select.append($('