text: """
  IP Pool Maker for Cisco Intersight, v2.0
  Author: Ugo Emekauwa
  Contact: uemekauw@cisco.com, uemekauwa@gmail.com
  Summary: The IP Pool Maker for Cisco Intersight automates the creation
           of IP Pools.
  GitHub Repository: https://github.com/ugo-emekauwa/cisco-imm-automation-tools
  """
  
  
  ########################
  # MODULE REQUIREMENT 1 #
  ########################
  """
  For the following variable below named key_id, please fill in between
  the quotes your Intersight API Key ID.
  
  Here is an example:
  key_id = "5c89885075646127773ec143/5c82fc477577712d3088eb2f/5c8987b17577712d302eaaff"
  
  """
  key_id = ""
  
  
  ########################
  # MODULE REQUIREMENT 2 #
  ########################
  """
  For the following variable below named key, please fill in between
  the quotes your system's file path to your Intersight API key "SecretKey.txt"
  file.
  
  Here is an example:
  key = "C:\\Users\\demouser\\Documents\\SecretKey.txt"
  
  """
  key = ""
  
  
  ########################
  # MODULE REQUIREMENT 3 #
  ########################
  """
  Provide the required configuration settings to create the
  IP Pool on Cisco Intersight. Remove the sample
  values and replace them with your own, where applicable.
  """
  
  ####### Start Configuration Settings - Provide values for the variables listed below. #######
  
  # General Settings
  ip_pool_name = "IP-Pool-1"
  ip_pool_description = "A Cisco Intersight IP Pool generated by the IP Pool Maker."
  ip_pool_organization = "default"
  ip_pool_tags = {"Org": "IT", "Dept": "DevOps"}  # Empty the ip_pool_tags dictionary if no tags are needed, for example: ip_pool_tags = {}
  
  # Pool Detail Settings
  configure_ipv4_pool = True
  ipv4_netmask = "255.255.255.0"
  ipv4_gateway = "192.168.0.1"
  ipv4_primary_dns = "192.168.133.1"
  ipv4_secondary_dns = ""
  configure_ipv6_pool = False
  ipv6_prefix = 0
  ipv6_gateway = ""
  ipv6_primary_dns = ""
  ipv6_secondary_dns = ""
  
  ## NOTE! - For the IP blocks lists, place each IP block dictionary in the list to match the desired order (first to last).
  ipv4_blocks_list = [
      {"From": "192.168.0.20", "Size": 100},
      ]
  ipv6_blocks_list = []
  
  # Pool Default Settings (Change only if needed)
  ip_assignment_order = "default"       # Options: "default", "sequential"
  
  # Intersight Base URL Setting (Change only if using the Intersight Virtual Appliance)
  intersight_base_url = "https://www.intersight.com/api/v1"
  
  ####### Finish Configuration Settings - The required value entries are complete. #######
  
  
  #############################################################################################################################
  #############################################################################################################################
  
  
  import sys
  import traceback
  import json
  import copy
  import intersight
  import re
  
  # Function to get Intersight API client as specified in the Intersight Python SDK documentation for OpenAPI 3.x
  ## Modified to align with overall formatting and try/except blocks added for additional error handling
  def get_api_client(api_key_id,
                     api_secret_file,
                     endpoint="https://intersight.com"
                     ):
      try:
          with open(api_secret_file, 'r') as f:
              api_key = f.read()
          
          if re.search('BEGIN RSA PRIVATE KEY', api_key):
              # API Key v2 format
              signing_algorithm = intersight.signing.ALGORITHM_RSASSA_PKCS1v15
              signing_scheme = intersight.signing.SCHEME_RSA_SHA256
              hash_algorithm = intersight.signing.HASH_SHA256
  
          elif re.search('BEGIN EC PRIVATE KEY', api_key):
              # API Key v3 format
              signing_algorithm = intersight.signing.ALGORITHM_ECDSA_MODE_DETERMINISTIC_RFC6979
              signing_scheme = intersight.signing.SCHEME_HS2019
              hash_algorithm = intersight.signing.HASH_SHA256
  
          configuration = intersight.Configuration(
              host=endpoint,
              signing_info=intersight.signing.HttpSigningConfiguration(
                  key_id=api_key_id,
                  private_key_path=api_secret_file,
                  signing_scheme=signing_scheme,
                  signing_algorithm=signing_algorithm,
                  hash_algorithm=hash_algorithm,
                  signed_headers=[
                      intersight.signing.HEADER_REQUEST_TARGET,
                      intersight.signing.HEADER_HOST,
                      intersight.signing.HEADER_DATE,
                      intersight.signing.HEADER_DIGEST,
                      ]
                  )
              )
      except Exception:
          print("\nA configuration error has occurred!\n")
          print("Unable to access the Intersight API Key.")
          print("Exiting due to the Intersight API Key being unavailable.\n")
          print("Please verify that the correct API Key ID and API Key have "
                "been entered, then re-attempt execution.\n")
          print("Exception Message: ")
          traceback.print_exc()
          sys.exit(0)
          
      return intersight.ApiClient(configuration)
  
  
  # Establish function to test for the availability of the Intersight API and Intersight account
  def test_intersight_api_service(intersight_api_key_id,
                                  intersight_api_key,
                                  intersight_base_url="https://www.intersight.com/api/v1",
                                  preconfigured_api_client=None
                                  ):
      """This is a function to test the availability of the Intersight API and
      Intersight account. The tested Intersight account contains the user who is
      the owner of the provided Intersight API Key and Key ID.
  
      Args:
          intersight_api_key_id (str):
              The ID of the Intersight API key.
          intersight_api_key (str):
              The system file path of the Intersight API key.
          intersight_base_url (str):
              Optional; The base URL for Intersight API paths. The default value
              is "https://www.intersight.com/api/v1". This value typically only
              needs to be changed if using the Intersight Virtual Appliance. The
              default value is "https://www.intersight.com/api/v1".
          preconfigured_api_client ("ApiClient"):
              Optional; An ApiClient class instance which handles
              Intersight client-server communication through the use of API keys.
              The default value is None. If a preconfigured_api_client argument
              is provided, empty strings ("") or None can be provided for the
              intersight_api_key_id, intersight_api_key, and intersight_base_url
              arguments.
  
      Returns:
          A string of the name for the Intersight account tested, verifying the
          Intersight API service is up and the Intersight account is accessible.
          
      Raises:
          Exception:
              An exception occurred due to an issue with the provided API Key
              and/or API Key ID.
      """
      # Define Intersight SDK ApiClient variable
      if preconfigured_api_client is None:
          api_client = get_api_client(api_key_id=intersight_api_key_id,
                                      api_secret_file=intersight_api_key,
                                      endpoint=intersight_base_url
                                      )
      else:
          api_client = preconfigured_api_client
      try:
          # Check that Intersight Account is accessible
          print("Testing access to the Intersight API by verifying the "
                "Intersight account information...")
          api_client.call_api(resource_path="/iam/Accounts",
                              method="GET",
                              auth_settings=['cookieAuth', 'http_signature', 'oAuth2', 'oAuth2']
                              )
          response = api_client.last_response.data
          iam_account = json.loads(response)
          if api_client.last_response.status != 200:
              print("\nThe Intersight API and Account Availability Test did not "
                    "pass.")
              print("The Intersight account information could not be verified.")
              print("Exiting due to the Intersight account being unavailable.\n")
              print("Please verify that the correct API Key ID and API Key have "
                    "been entered, then re-attempt execution.\n")
              sys.exit(0)
          else:
              intersight_account_name = iam_account["Results"][0]["Name"]
              print("The Intersight API and Account Availability Test has "
                    "passed.\n")
              print(f"The Intersight account named '{intersight_account_name}' "
                    "has been found.")
              return intersight_account_name
      except Exception:
          print("\nA configuration error has occurred!\n")
          print("Unable to access the Intersight API.")
          print("Exiting due to the Intersight API being unavailable.\n")
          print("Please verify that the correct API Key ID and API Key have "
                "been entered, then re-attempt execution.\n")
          print("Exception Message: ")
          traceback.print_exc()
          sys.exit(0)
  
  
  # Establish function to retrieve the MOID of a specific Intersight API object by name
  def intersight_object_moid_retriever(intersight_api_key_id,
                                       intersight_api_key,
                                       object_name,
                                       intersight_api_path,
                                       object_type="object",
                                       organization="default",
                                       intersight_base_url="https://www.intersight.com/api/v1",
                                       preconfigured_api_client=None
                                       ):
      """This is a function to retrieve the MOID of Intersight objects
      using the Intersight API.
  
      Args:
          intersight_api_key_id (str):
              The ID of the Intersight API key.
          intersight_api_key (str):
              The system file path of the Intersight API key.
          object_name (str):
              The name of the Intersight object.
          intersight_api_path (str):
              The Intersight API path of the Intersight object.
          object_type (str):
              Optional; The type of Intersight object. The default value is
              "object".
          organization (str):
              Optional; The Intersight organization of the Intersight object.
              The default value is "default".
          intersight_base_url (str):
              Optional; The base URL for Intersight API paths. The default value
              is "https://www.intersight.com/api/v1". This value typically only
              needs to be changed if using the Intersight Virtual Appliance.
          preconfigured_api_client ("ApiClient"):
              Optional; An ApiClient class instance which handles
              Intersight client-server communication through the use of API keys.
              The default value is None. If a preconfigured_api_client argument
              is provided, empty strings ("") or None can be provided for the
              intersight_api_key_id, intersight_api_key, and intersight_base_url
              arguments.
  
      Returns:
          A string of the MOID for the provided Intersight object.
          
      Raises:
          Exception:
              An exception occurred due to an issue accessing the Intersight API
              path. The status code or error message will be specified.
      """
      # Define Intersight SDK ApiClient variable
      if preconfigured_api_client is None:
          api_client = get_api_client(api_key_id=intersight_api_key_id,
                                      api_secret_file=intersight_api_key,
                                      endpoint=intersight_base_url
                                      )
      else:
          api_client = preconfigured_api_client
      try:
          # Retrieve the Intersight Account name
          api_client.call_api(resource_path="/iam/Accounts",
                              method="GET",
                              auth_settings=['cookieAuth', 'http_signature', 'oAuth2', 'oAuth2']
                              )
          response = api_client.last_response.data
          iam_account = json.loads(response)
          if api_client.last_response.status != 200:
              print("The provided Intersight account information could not be "
                    "accessed.")
              print("Exiting due to the Intersight account being unavailable.\n")
              print("Please verify that the correct API Key ID and API Key have "
                    "been entered, then re-attempt execution.\n")
              sys.exit(0)
          else:
              intersight_account_name = iam_account["Results"][0]["Name"]
      except Exception:
          print("\nA configuration error has occurred!\n")
          print("Unable to access the Intersight API.")
          print("Exiting due to the Intersight API being unavailable.\n")
          print("Please verify that the correct API Key ID and API Key have "
                "been entered, then re-attempt execution.\n")
          sys.exit(0)
      # Retrieving the provided object from Intersight...
      full_intersight_api_path = f"/{intersight_api_path}"
      try:
          api_client.call_api(resource_path=full_intersight_api_path,
                              method="GET",
                              auth_settings=['cookieAuth', 'http_signature', 'oAuth2', 'oAuth2']
                              )
          response = api_client.last_response.data
          intersight_objects = json.loads(response)
          # The Intersight API resource path has been accessed successfully.
      except Exception:
          print("\nA configuration error has occurred!\n")
          print("There was an issue retrieving the "
                f"{object_type} from Intersight.")
          print("Unable to access the provided Intersight API resource path "
                f"'{intersight_api_path}'.")
          print("Please review and resolve any error messages, then re-attempt "
                "execution.\n")
          print("Exception Message: ")
          traceback.print_exc()
          sys.exit(0)
  
      if intersight_objects.get("Results"):
          for intersight_object in intersight_objects.get("Results"):
              if intersight_object.get("Organization"):
                  provided_organization_moid = intersight_object_moid_retriever(intersight_api_key_id=None,
                                                                                intersight_api_key=None,
                                                                                object_name=organization,
                                                                                intersight_api_path="organization/Organizations",
                                                                                object_type="Organization",
                                                                                preconfigured_api_client=api_client
                                                                                )
                  if intersight_object.get("Organization", {}).get("Moid") == provided_organization_moid:
                      if intersight_object.get("Name") == object_name:
                          intersight_object_moid = intersight_object.get("Moid")
                          # The provided object and MOID has been identified and retrieved.
                          return intersight_object_moid
              else:
                  if intersight_object.get("Name") == object_name:
                      intersight_object_moid = intersight_object.get("Moid")
                      # The provided object and MOID has been identified and retrieved.
                      return intersight_object_moid
          else:
              print("\nA configuration error has occurred!\n")
              print(f"The provided {object_type} named '{object_name}' was not "
                    "found.")
              print("Please check the Intersight Account named "
                    f"{intersight_account_name}.")
              print("Verify through the API or GUI that the needed "
                    f"{object_type} is present.")
              print(f"If the needed {object_type} is missing, please create it.")
              print("Once the issue has been resolved, re-attempt execution.\n")
              sys.exit(0)
      else:
          print("\nA configuration error has occurred!\n")
          print(f"The provided {object_type} named '{object_name}' was not "
                "found.")
          print(f"No requested {object_type} instance is currently available in "
                f"the Intersight account named {intersight_account_name}.")
          print("Please check the Intersight Account named "
                f"{intersight_account_name}.")
          print(f"Verify through the API or GUI that the needed {object_type} "
                "is present.")
          print(f"If the needed {object_type} is missing, please create it.")
          print("Once the issue has been resolved, re-attempt execution.\n")
          sys.exit(0)
  
  
  class IpPool:
      """This class is used to configure an IP Pool in Intersight.
      """
      object_type = "IP Pool"
      intersight_api_path = "ippool/Pools"
      subobject_types = None
      subobject_attribute_maps = None
      object_variable_value_maps = [
          {"VariableName": "pool_assignment_order",
           "Description": "IP Pool Assignment Order",
           "AttributeName": "AssignmentOrder",
           "Values": [
               {"FrontEndValue": "Default",
                "BackEndValue": "default"
                },
               {"FrontEndValue": "Sequential",
                "BackEndValue": "sequential"
                }
               ]
           }
          ]
      block_types = [
          {"BlockType": "ippool.IpV4Block",
           "BlocksListVariableName": "ipv4_blocks_list",
           "Description": "IPv4 Block",
           "AttributeName": "IpV4Blocks"
           },
          {"BlockType": "ippool.IpV6Block",
           "BlocksListVariableName": "ipv6_blocks_list",
           "Description": "IPv6 Block",
           "AttributeName": "IpV6Blocks"
           }
          ]
      
      def __init__(self,
                   intersight_api_key_id,
                   intersight_api_key,
                   pool_name,
                   configure_ipv4_pool,
                   ipv4_netmask,
                   ipv4_gateway,
                   ipv4_primary_dns,
                   ipv4_secondary_dns,
                   ipv4_blocks_list,
                   configure_ipv6_pool=False,
                   ipv6_prefix=0,
                   ipv6_gateway="",
                   ipv6_primary_dns="",
                   ipv6_secondary_dns="",
                   ipv6_blocks_list=None,
                   pool_description="",
                   pool_assignment_order="default",
                   organization="default",
                   intersight_base_url="https://www.intersight.com/api/v1",
                   tags=None,
                   preconfigured_api_client=None
                   ):
          self.intersight_api_key_id = intersight_api_key_id
          self.intersight_api_key = intersight_api_key
          self.pool_name = pool_name
          self.configure_ipv4_pool = configure_ipv4_pool
          self.ipv4_netmask = ipv4_netmask
          self.ipv4_gateway = ipv4_gateway
          self.ipv4_primary_dns = ipv4_primary_dns
          self.ipv4_secondary_dns = ipv4_secondary_dns
          self.ipv4_blocks_list = ipv4_blocks_list
          self.configure_ipv6_pool = configure_ipv6_pool
          self.ipv6_prefix = ipv6_prefix
          self.ipv6_gateway = ipv6_gateway
          self.ipv6_primary_dns = ipv6_primary_dns
          self.ipv6_secondary_dns = ipv6_secondary_dns
          if ipv6_blocks_list is None:
              self.ipv6_blocks_list = []
          else:
              self.ipv6_blocks_list = ipv6_blocks_list
          self.pool_description = pool_description
          self.pool_assignment_order = pool_assignment_order
          self.organization = organization
          self.intersight_base_url = intersight_base_url
          if tags is None:
              self.tags = {}
          else:
              self.tags = tags
          if preconfigured_api_client is None:
              self.api_client = get_api_client(api_key_id=intersight_api_key_id,
                                               api_secret_file=intersight_api_key,
                                               endpoint=intersight_base_url
                                               )
          else:
              self.api_client = preconfigured_api_client
          self.intersight_api_body = {
              "Name": self.pool_name,
              "Description": self.pool_description
              }
  
      def __repr__(self):
          return (
              f"{self.__class__.__name__}"
              f"('{self.intersight_api_key_id}', "
              f"'{self.intersight_api_key}', "
              f"'{self.pool_name}', "
              f"{self.configure_ipv4_pool}, "
              f"'{self.ipv4_netmask}', "
              f"'{self.ipv4_gateway}', "
              f"'{self.ipv4_primary_dns}', "
              f"'{self.ipv4_secondary_dns}', "
              f"{self.ipv4_blocks_list}, "
              f"{self.configure_ipv6_pool}, "
              f"'{self.ipv6_prefix}', "
              f"'{self.ipv6_gateway}', "
              f"'{self.ipv6_primary_dns}', "
              f"'{self.ipv6_secondary_dns}', "
              f"{self.ipv6_blocks_list}, "
              f"'{self.pool_description}', "
              f"'{self.pool_assignment_order}', "
              f"'{self.organization}', "
              f"'{self.intersight_base_url}', "
              f"{self.tags}, "
              f"{self.api_client})"
              )
  
      def __str__(self):
          return f"{self.__class__.__name__} class object for '{self.pool_name}'"
  
      def _post_intersight_object(self):
          """This is a function to configure an Intersight object by
          performing a POST through the Intersight API.
  
          Returns:
              A string with a statement indicating whether the POST method
              was successful or failed.
              
          Raises:
              Exception:
                  An exception occurred while performing the API call.
                  The status code or error message will be specified.
          """
          full_intersight_api_path = f"/{self.intersight_api_path}"
          try:
              self.api_client.call_api(resource_path=full_intersight_api_path,
                                       method="POST",
                                       body=self.intersight_api_body,
                                       auth_settings=['cookieAuth', 'http_signature', 'oAuth2', 'oAuth2']
                                       )
              print(f"The configuration of the base {self.object_type} "
                    "has completed.")
              return "The POST method was successful."
          except intersight.exceptions.ApiException as error:
              if error.status == 409:
                  existing_intersight_object_name = self.intersight_api_body.get("Name", "object")
                  print(f"The targeted {self.object_type} appears to already "
                        "exist.")
                  print("An attempt will be made to update the pre-existing "
                        f"{existing_intersight_object_name}...")
                  try:
                      existing_intersight_object_moid = intersight_object_moid_retriever(intersight_api_key_id=None,
                                                                                         intersight_api_key=None,
                                                                                         object_name=existing_intersight_object_name,
                                                                                         intersight_api_path=self.intersight_api_path,
                                                                                         object_type=self.object_type,
                                                                                         preconfigured_api_client=self.api_client
                                                                                         )
                      # Update full Intersight API path with the MOID of the existing object
                      full_intersight_api_path_with_moid = f"/{self.intersight_api_path}/{existing_intersight_object_moid}"
                      self.api_client.call_api(resource_path=full_intersight_api_path_with_moid,
                                               method="POST",
                                               body=self.intersight_api_body,
                                               auth_settings=['cookieAuth', 'http_signature', 'oAuth2', 'oAuth2']
                                               )
                      print(f"The update of the {self.object_type} has "
                            "completed.")
                      print(f"The pre-existing {existing_intersight_object_name} "
                            "has been updated.")
                      return "The POST method was successful."
                  except Exception:
                      print("\nA configuration error has occurred!\n")
                      print(f"Unable to update the {self.object_type} under the "
                            "Intersight API resource path "
                            f"'{full_intersight_api_path_with_moid}'.\n")
                      print(f"The pre-existing {existing_intersight_object_name} "
                            "could not be updated.")
                      print("Exception Message: ")
                      traceback.print_exc()
                      return "The POST method failed."
              else:
                  print("\nA configuration error has occurred!\n")
                  print(f"Unable to configure the {self.object_type} under the "
                        "Intersight API resource path "
                        f"'{full_intersight_api_path}'.\n")
                  print("Exception Message: ")
                  traceback.print_exc()
                  return "The POST method failed."
          except Exception:
              print("\nA configuration error has occurred!\n")
              print(f"Unable to configure the {self.object_type} under the "
                    "Intersight API resource path "
                    f"'{full_intersight_api_path}'.\n")
              print("Exception Message: ")
              traceback.print_exc()
              return "The POST method failed."
  
      def _update_api_body_general_attributes(self):
          """This function updates the Intersight API body with general
          attributes for the Intersight object.
          """
          # Retrieve the Intersight Organization MOID
          pool_organization_moid = intersight_object_moid_retriever(intersight_api_key_id=None,
                                                                      intersight_api_key=None,
                                                                      object_name=self.organization,
                                                                      intersight_api_path="organization/Organizations",
                                                                      object_type="Organization",
                                                                      preconfigured_api_client=self.api_client
                                                                      )
          # Update the API body with the Intersight Organization MOID
          self.intersight_api_body["Organization"] = {"Moid": pool_organization_moid}
          # Create the Intersight Tags dictionary list
          tags_dictionary_list = []
          if self.tags:
              for key in self.tags:
                  tags_dictionary_list_entry = {
                      "Key": key,
                      "Value": self.tags.get(key)
                      }
                  tags_dictionary_list.append(tags_dictionary_list_entry)
          # Update the API body with the Intersight Tags dictionary list
          self.intersight_api_body["Tags"] = tags_dictionary_list
  
      def _update_api_body_subobject_attributes(self):
          """This function updates the Intersight API body with individual
          attributes for subobjects of the Intersight object.
  
          Raises:
              Exception:
                  An exception occurred while reformatting a provided value for
                  an attribute. The issue will likely be due to the provided
                  value not being in string format. Changing the value to string
                  format should resolve the exception.
          """        
          def attribute_map_handler(attribute_map_dictionary):
              """This is a function to handle attributes with a mismatch in the
              Front-End Name and Back-End Name.
  
              Args:
                  attribute_map_dictionary (dict):
                      A dictionary containing attribute data, including front-end
                      (GUI) to back-end (API) mapped values.
              """
              # Establish default automatic insertion status of current attribute
              automatic_insertion_of_attribute_value_performed = False
              # Check if current attribute is mandatory
              if attribute_map_dictionary.get("Mandatory"):
                  if not any(
                      attribute_name_key in
                      staged_subobject_dictionary for
                      attribute_name_key in
                      (attribute_map_dictionary["FrontEndName"],
                       attribute_map_dictionary["BackEndName"]
                       )
                      ):
                      if attribute_map_dictionary.get("AutomaticInsertion"):
                          staged_subobject_dictionary[attribute_map_dictionary["BackEndName"]] = attribute_map_dictionary.get("AutomaticInsertionValue")
                          automatic_insertion_of_attribute_value_performed = True
                      else:
                          print("\nA configuration error has occurred!\n")
                          print("During the configuration of the "
                                f"{subobject_type_dictionary['Description']} "
                                f"settings for the {self.object_type} named "
                                f"{self.pool_name}, there was an issue "
                                "accessing the value for the "
                                f"{attribute_map_dictionary['Description']}.")
                          print("Please verify the following key exists in the "
                                "appropriate dictionary of the "
                                f"{subobject_type_dictionary['Description']} "
                                f"list variable for the {self.object_type}, then "
                                "re-attempt execution:\n")
                          if attribute_map_dictionary['FrontEndName']:
                              print(f"'{attribute_map_dictionary['FrontEndName']}'\n")
                          else:
                              print(f"'{attribute_map_dictionary['BackEndName']}'\n")
                          sys.exit(0)
              # Check for attribute front-end name key in the dictionary
              if attribute_map_dictionary["FrontEndName"] in staged_subobject_dictionary:
                  # If the attribute back-end name key is not present in the dictionary, insert the back-end name key with the front-end name key value
                  if attribute_map_dictionary["BackEndName"] not in staged_subobject_dictionary:
                      staged_subobject_dictionary[attribute_map_dictionary["BackEndName"]] = staged_subobject_dictionary.get(attribute_map_dictionary["FrontEndName"])
                  # Remove the front-end name key from the dictionary
                  staged_subobject_dictionary.pop(attribute_map_dictionary["FrontEndName"])
              # Check for front-end to back-end value mapping
              if attribute_map_dictionary.get("FronttoBackEndValueMaps"):
                  if (
                      attribute_map_dictionary["BackEndName"] in
                      staged_subobject_dictionary and
                      not automatic_insertion_of_attribute_value_performed
                      ):
                      # Retrieve the provided attribute value
                      provided_attribute_value = staged_subobject_dictionary.get(attribute_map_dictionary["BackEndName"])
                      # Reformat the provided attribute value to lowercase and remove spaces to prevent potential format issues
                      try:
                          provided_attribute_value_reformatted = "".join(provided_attribute_value.lower().split())
                      except Exception:
                          print("\nA configuration error has occurred!\n")
                          print("During the configuration of the "
                                f"{subobject_type_dictionary['Description']} "
                                f"settings for the {self.object_type} named "
                                f"{self.pool_name}, there was an issue with "
                                "the value for the "
                                f"{attribute_map_dictionary['Description']}.")
                          print("The value provided was "
                                f"{provided_attribute_value}.")
                          print("Please verify that the value has been provided "
                                "in an accepted string format.")
                          print("Please review and resolve any error messages, "
                                "then re-attempt execution.\n")
                          sys.exit(0)
                      # Create list of known and mapped front-end to back-end values
                      front_to_backend_value_maps_key_list = list(attribute_map_dictionary["FronttoBackEndValueMaps"])
                      # Replace known and reformatted front-end value with known and mapped back-end value
                      if provided_attribute_value_reformatted in front_to_backend_value_maps_key_list:
                          provided_attribute_value_mapped = attribute_map_dictionary["FronttoBackEndValueMaps"][provided_attribute_value_reformatted]
                          staged_subobject_dictionary[attribute_map_dictionary["BackEndName"]] = provided_attribute_value_mapped
                      else:
                          print("\nWARNING: An unknown "
                                f"{attribute_map_dictionary['Description']} "
                                f"value of '{provided_attribute_value}' has been "
                                "provided for the "
                                f"{subobject_type_dictionary['Description']} "
                                "settings!")
                          print("An attempt will be made to configure the "
                                "unknown "
                                f"{attribute_map_dictionary['Description']} "
                                "value.")
                          print("If there is an error, please use one of the "
                                "following known values for the "
                                f"{attribute_map_dictionary['Description']}, "
                                "then re-attempt execution:\n")
                          print(*attribute_map_dictionary["FixedFrontEndValues"],
                                sep=", "
                                )
          # Check for subobject types that may need configuration
          if self.subobject_types:
              for subobject_type_dictionary in self.subobject_types:
                  subobject_list = getattr(self,
                                           subobject_type_dictionary.get("SubobjectList")
                                           )
                  if subobject_list:
                      converted_subobject_list = []
                      for subobject_dictionary in subobject_list:
                          staged_subobject_dictionary = copy.deepcopy(subobject_dictionary)
                          returned_subobject_type = subobject_type_dictionary["SubobjectType"]
                          # Set 'ClassId' attribute
                          staged_subobject_dictionary["ClassId"] = returned_subobject_type
                          # Set 'ObjectType' attribute
                          staged_subobject_dictionary["ObjectType"] = returned_subobject_type
                          # Handle setting of attributes which have mismatched Front-End and Back-End names
                          for attribute_map_dictionary in self.subobject_attribute_maps.get(returned_subobject_type):
                              attribute_map_handler(attribute_map_dictionary)
                          converted_subobject_dictionary = staged_subobject_dictionary
                          converted_subobject_list.append(converted_subobject_dictionary)
                      # Update Intersight API body with the converted sub-objects list
                      self.intersight_api_body[subobject_type_dictionary["AttributeName"]] = converted_subobject_list
  
      def _update_api_body_mapped_object_attributes(self):
          """This function updates the Intersight API body with individual
          attributes that require mapping frontend to backend values for
          compatibility with the Intersight API.
  
          Raises:
              Exception:
                  An exception occurred while reformatting a provided value for
                  an attribute. The issue will likely be due to the provided
                  value not being in string format. Changing the value to string
                  format should resolve the exception.
          """
          # Check for object variables with value maps that need configuration
          if self.object_variable_value_maps:
              for object_variable in self.object_variable_value_maps:
                  # Create list of all known and accepted frontend values
                  all_known_and_accepted_frontend_values = (object_variable_value["FrontEndValue"]
                                                            for
                                                            object_variable_value
                                                            in
                                                            object_variable["Values"]
                                                            )
                  # Retrieve the user provided object variable value
                  provided_object_variable_value = getattr(self,
                                                           object_variable["VariableName"]
                                                           )
                  # Reformat the user provided object variable value to lowercase and remove spaces to prevent potential format issues
                  try:
                      reformatted_object_variable_value = "".join(provided_object_variable_value.lower().split())
                  except Exception:
                      print("\nA configuration error has occurred!\n")
                      print(f"During the configuration of the {self.object_type} named "
                            f"{self.pool_name}, there was an issue with the value "
                            f"provided for the {object_variable['Description']} setting.")
                      print(f"The value provided was {provided_object_variable_value}.")
                      print("To proceed, the value provided for the "
                            f"{object_variable['Description']} setting should be updated to "
                            "an accepted string format.")
                      print("The recommended values are the following:\n")
                      # Print list of all known and accepted frontend values for user
                      print(*all_known_and_accepted_frontend_values,
                            sep=", "
                            )
                      print("\nPlease update the configuration, then re-attempt "
                            "execution.\n")
                      sys.exit(0)
                  # Cycle through known values and match provided object variable value to backend value
                  for object_variable_value in object_variable["Values"]:
                      # Create list of all known and accepted frontend and backend values
                      current_known_frontend_and_backend_value_options = (object_variable_value.values())
                      # Retrieve the current known backend value
                      current_known_backend_value = object_variable_value["BackEndValue"]
                      if (
                          reformatted_object_variable_value
                          in
                          ("".join(current_known_frontend_or_backend_value.lower().split())
                           for
                           current_known_frontend_or_backend_value
                           in
                           current_known_frontend_and_backend_value_options
                           )
                          ):
                          backend_object_variable_value = current_known_backend_value
                          break
                  else:
                      # If no backend match is found with the user provided object variable value, pass on the user provided object variable value to Intersight to decide
                      print(f"\nWARNING: An unknown {self.object_type} value of "
                            f"'{provided_object_variable_value}' has been "
                            f"provided for the {object_variable['Description']} "
                            "settings!")
                      print("An attempt will be made to configure the unknown "
                            f"{object_variable['Description']} value.")
                      print("If there is an error, please use one of the "
                            "following known values for the "
                            f"{object_variable['Description']} settings, then "
                            "re-attempt execution:\n")
                      print(*all_known_and_accepted_frontend_values,
                            sep=", "
                            )
                      backend_object_variable_value = provided_object_variable_value
                  # Update Intersight API body with the converted object variable value
                  self.intersight_api_body[object_variable["AttributeName"]] = backend_object_variable_value
  
      def _update_api_body_pool_block_attributes(self):
          """This function updates the Intersight API body with the Pool block
          attributes for the Intersight object. The provided Pool block list is 
          converted for use with the Intersight API.
          """
          for block_type_dictionary in self.block_types:
              blocks_list = getattr(self,
                                    block_type_dictionary.get("BlocksListVariableName")
                                    )
              if blocks_list:
                  converted_blocks_list = []
                  for provided_block_dictionary in blocks_list:
                      staged_block_dictionary = copy.deepcopy(provided_block_dictionary)
                      returned_block_type = block_type_dictionary["BlockType"]
                      # Set 'ClassId' attribute
                      staged_block_dictionary["ClassId"] = returned_block_type
                      # Set 'ObjectType' attribute
                      staged_block_dictionary["ObjectType"] = returned_block_type
                      converted_block_dictionary = staged_block_dictionary
                      converted_blocks_list.append(converted_block_dictionary)
                  # Update Intersight API body with the converted Pool block list value
                  self.intersight_api_body[block_type_dictionary["AttributeName"]] = converted_blocks_list
  
      def object_maker(self):
          """This function makes the targeted pool object.
          """           
          print(f"\nConfiguring the {self.object_type} named "
                f"{self.pool_name}...")
          # Configure IPv4 Pool settings
          if self.configure_ipv4_pool:
              # Set IPv4 configuration dictionary
              ipv4_config_dictionary = {
                  "Netmask": self.ipv4_netmask,
                  "Gateway": self.ipv4_gateway,
                  "PrimaryDns": self.ipv4_primary_dns,
                  "SecondaryDns": self.ipv4_secondary_dns,
                  "ClassId": "ippool.IpV4Config",
                  "ObjectType": "ippool.IpV4Config"
                  }
              # Update the API body with the IPv4 Pool configuration dictionary
              self.intersight_api_body["IpV4Config"] = ipv4_config_dictionary
          # Configure IPv6 Pool settings
          if self.configure_ipv6_pool:
              # Set IPv6 configuration dictionary
              ipv6_config_dictionary = {
                  "Prefix": self.ipv6_prefix,
                  "Gateway": self.ipv6_gateway,
                  "PrimaryDns": self.ipv6_primary_dns,
                  "SecondaryDns": self.ipv6_secondary_dns,
                  "ClassId": "ippool.IpV6Config",
                  "ObjectType": "ippool.IpV6Config"
                  }
              # Update the API body with the IPv6 Pool configuration dictionary
              self.intersight_api_body["IpV6Config"] = ipv6_config_dictionary
          # Update the API body with general attributes
          self._update_api_body_general_attributes()
          # Update the API body with individual subobject attributes
          self._update_api_body_subobject_attributes()
          # Update the API body with individual mapped object attributes
          self._update_api_body_mapped_object_attributes()
          # Update the API body with individual Pool block list attributes
          self._update_api_body_pool_block_attributes()
          # POST the API body to Intersight
          self._post_intersight_object()
  
  
  def ip_pool_maker(intersight_api_key_id,
                    intersight_api_key,
                    pool_name,
                    configure_ipv4_pool,
                    ipv4_netmask,
                    ipv4_gateway,
                    ipv4_primary_dns,
                    ipv4_secondary_dns,
                    ipv4_blocks_list,
                    configure_ipv6_pool=False,
                    ipv6_prefix=0,
                    ipv6_gateway="",
                    ipv6_primary_dns="",
                    ipv6_secondary_dns="",
                    ipv6_blocks_list=None,
                    pool_description="",
                    pool_assignment_order="default",
                    organization="default",
                    intersight_base_url="https://www.intersight.com/api/v1",
                    tags=None,
                    preconfigured_api_client=None
                    ):
      """This is a function used to make an IP Pool on Cisco Intersight.
  
      Args:
          intersight_api_key_id (str):
              The ID of the Intersight API key.
          intersight_api_key (str):
              The system file path of the Intersight API key.
          pool_name (str):
              The name of the pool to be created.
          configure_ipv4_pool (bool):
              Enable or disable configuring IP version 4 pools.
          ipv4_netmask (str):
              The netmask or subnet address of the IPv4 pools to be created.
          ipv4_gateway (str):
              The gateway of the IPv4 pools to be created.
          ipv4_primary_dns (str):
              The primary DNS server of the IPv4 pools to be created.
          ipv4_secondary_dns (str):
              The secondary DNS server of the IPv4 pools to be created.
          ipv4_blocks_list (list):
              The list of dictionary entries for each IPv4 block to
              be configured. An example entry is:
              [{"From": "192.168.0.20",
                "Size": 100},].
              A "From" key with a string value must be configured to specify the
              first IPv4 address in the block. A "Size" key with an integer value
              must also be configured to specify the size of the block. Optional
              key settings such as "To" can also be configured in each list
              dictionary, if the associated system default values are not
              desired. Multiple dictionaries can be placed in the list to
              configure different block ranges.
          configure_ipv6_pool (bool):
              Optional; Enable or disable configuring IP version 6 pools. The
              default value is False.
          ipv6_prefix (int):
              Optional; The prefix of the IPv6 pools to be created. The default
              value is 0.
          ipv6_gateway (str):
              Optional; The gateway of the IPv6 pools to be created. The default
              value is an empty string ("").
          ipv6_primary_dns (str):
              Optional; The primary DNS server of the IPv6 pools to be created.
              The default value is an empty string ("").
          ipv6_secondary_dns (str):
              Optional; The secondary DNS server of the IPv6 pools to be created.
              The default value is an empty string ("").
          ipv6_blocks_list (list):
              Optional; The list of dictionary entries for each IPv6 block to
              be configured. The default value is None. An
              example entry is:
              [{"From": "2001:DB8:1234::10",
                "Size": 100},].
              A "From" key with a string value must be configured to specify the
              first IPv6 address in the block. A "Size" key with an integer value
              must also be configured to specify the size of the block. Optional
              key settings such as "To" can also be configured in each list
              dictionary, if the associated system default values are not
              desired. Multiple dictionaries can be placed in the list to
              configure different block ranges.
          pool_description (str):
              Optional; The description of the pool to be created. The default
              value is an empty string ("").
          pool_assignment_order (str):
              Optional; The assignment order of identifiers in the pool.
              Available options are "sequential" for sequential assignment of
              identifiers and "default" which leaves assignment order up to the
              system. The default value is "default".
          organization (str):
              Optional; The Intersight account organization of the pool.
              The default value is "default".
          intersight_base_url (str):
              Optional; The base URL for Intersight API paths. The default value
              is "https://www.intersight.com/api/v1". This value typically only
              needs to be changed if using the Intersight Virtual Appliance.
          tags (dict):
              Optional; The Intersight account tags that will be assigned to the
              pool. The default value is None.
          preconfigured_api_client ("ApiClient"):
              Optional; An ApiClient class instance which handles
              Intersight client-server communication through the use of API keys.
              The default value is None. If a preconfigured_api_client argument
              is provided, empty strings ("") or None can be provided for the
              intersight_api_key_id, intersight_api_key, and intersight_base_url
              arguments.
      """
      
      def builder(target_object):
          """This is a function used to build the objects that are components of
          an overarching pool or pool on Cisco Intersight.
  
          Args:
              target_object (class):
                  The class representing the object to be built on Intersight.
  
          Raises:
              Exception:
                  An exception occurred due to an issue accessing the Intersight
                  API path. The status code or error message will be specified.
          """
          try:
              target_object.object_maker()
          except Exception:
              print("\nA configuration error has occurred!\n")
              print("The builder function failed to configure the "
                    f"{target_object.object_type} settings.")
              print("Please check the provided arguments for the "
                    f"{target_object.object_type} settings.\n")
              print("Exception Message: ")
              traceback.print_exc()
  
      # Define and create IP Pool object in Intersight
      builder(IpPool(intersight_api_key_id=intersight_api_key_id,
                     intersight_api_key=intersight_api_key,
                     pool_name=pool_name,
                     configure_ipv4_pool=configure_ipv4_pool,
                     ipv4_netmask=ipv4_netmask,
                     ipv4_gateway=ipv4_gateway,
                     ipv4_primary_dns=ipv4_primary_dns,
                     ipv4_secondary_dns=ipv4_secondary_dns,
                     ipv4_blocks_list=ipv4_blocks_list,
                     configure_ipv6_pool=configure_ipv6_pool,
                     ipv6_prefix=ipv6_prefix,
                     ipv6_gateway=ipv6_gateway,
                     ipv6_primary_dns=ipv6_primary_dns,
                     ipv6_secondary_dns=ipv6_secondary_dns,
                     ipv6_blocks_list=ipv6_blocks_list,
                     pool_description=pool_description,
                     pool_assignment_order=pool_assignment_order,
                     organization=organization,
                     intersight_base_url=intersight_base_url,
                     tags=tags,
                     preconfigured_api_client=preconfigured_api_client
                     ))
  
  
  def main():
      # Establish Maker specific variables
      maker_type = "Intersight IP Pool Maker"
      
      # Establish Intersight SDK for Python API client instance
      main_intersight_api_client = get_api_client(api_key_id=key_id,
                                                  api_secret_file=key,
                                                  endpoint=intersight_base_url
                                                  )
      
      # Starting the Pool Maker for Cisco Intersight
      print(f"\nStarting the {maker_type} for Cisco Intersight.\n")
  
      # Run the Intersight API and Account Availability Test
      print("Running the Intersight API and Account Availability Test.")
      test_intersight_api_service(
          intersight_api_key_id=None,
          intersight_api_key=None,
          preconfigured_api_client=main_intersight_api_client
          )
  
      # Create the Pool in Intersight
      ip_pool_maker(
          intersight_api_key_id=None,
          intersight_api_key=None,
          pool_name=ip_pool_name,
          configure_ipv4_pool=configure_ipv4_pool,
          ipv4_netmask=ipv4_netmask,
          ipv4_gateway=ipv4_gateway,
          ipv4_primary_dns=ipv4_primary_dns,
          ipv4_secondary_dns=ipv4_secondary_dns,
          ipv4_blocks_list=ipv4_blocks_list,
          configure_ipv6_pool=configure_ipv6_pool,
          ipv6_prefix=ipv6_prefix,
          ipv6_gateway=ipv6_gateway,
          ipv6_primary_dns=ipv6_primary_dns,
          ipv6_secondary_dns=ipv6_secondary_dns,
          ipv6_blocks_list=ipv6_blocks_list,
          pool_description=ip_pool_description,
          pool_assignment_order=ip_assignment_order,
          organization=ip_pool_organization,
          intersight_base_url=intersight_base_url,
          tags=ip_pool_tags,
          preconfigured_api_client=main_intersight_api_client
          )
  
      # Pool Maker completion
      print(f"\nThe {maker_type} has completed.\n")
  
  
  if __name__ == "__main__":
      main()
  
  # Exiting the Pool Maker for Cisco Intersight
  sys.exit(0)
  