#  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.

Namespaces:
  =: io.murano.resources
  std: io.murano
  sys: io.murano.system

Name: NeutronNetworkBase

Properties:
  port_security_disable:
    Contract: $.bool()

Extends: Network

Methods:
  .init:
    Body:
      - $._netExplorer: null
      - $._environment: $.find(std:Environment)

  _getNetExplorer:
    Body:
      - If: $._netExplorer = null
        Then:
          - $._netExplorer: new(sys:NetworkExplorer, $this.getRegion())
      - Return: $._netExplorer

  joinInstanceToNetwork:
    Arguments:
      - instance:
          Contract: $.class(Instance).notNull()
      - securityGroupName:
          Contract: $.string()
      - securityGroups:
          Contract: [$.string()]
      - sharedIps:
          Contract:
            - $.class(std:SharedIp)
      - netRef:
            Contract: $
      - subnetRef:
            Contract: $
      - floatingIpResourceName:
          Contract: $.string()
      - floatingIpNetRef:
          Contract: $
      - netName:
          Contract: $.string().notNull()
    Body:
      - $netExplorer: $._getNetExplorer()
      - $securityGroupsEnabled: $netExplorer.listNeutronExtensions().alias.contains('security-group')
      - $portName: format('port-{0}-{1}', id($), $instance.name)

      - $addressesOutputName: format('addresses-{0}-{1}', $instance.name, id($this))
      - $patchTemplate:
          resources:
            $portName:
              type: 'OS::Neutron::Port'
              properties:
                network: $netRef
                replacement_policy: AUTO
          outputs:
            $addressesOutputName:
              description: format('Addresses for {0} in {1}', $instance.name, $netName)
              value:
                get_attr: [$instance.name, 'addresses', $netName]

      - If: $subnetRef
        Then:
          - $template:
              resources:
                $portName:
                  properties:
                    fixed_ips:
                      - subnet: $subnetRef
          - $patchTemplate: $patchTemplate.mergeWith($template)

      - If: $securityGroupsEnabled and not $.port_security_disable
        Then:
          - If: len($securityGroups) > 0 and $securityGroups[0] != ""
            Then:
              - For: securityGroup
                In: $securityGroups
                Do:
                  - $template:
                      resources:
                        $portName:
                          properties:
                            security_groups:
                              - $securityGroup
                  - $patchTemplate: $patchTemplate.mergeWith($template)
            Else:
              - If: bool($securityGroupName)
                Then:
                  - $template:
                      resources:
                        $portName:
                          properties:
                            security_groups:
                              - get_resource: $securityGroupName
                  - $patchTemplate: $patchTemplate.mergeWith($template)

      - If: $.port_security_disable
        Then:
          - $template:
              resources:
                $portName:
                  properties:
                    port_security_enabled: false

          - $patchTemplate: $patchTemplate.mergeWith($template)

      - $instanceResources: [$portName]
      - $instanceOutputs: [$addressesOutputName]

      - For: sip
        In: $sharedIps
        Do:
          - $template:
              resources:
                $portName:
                  properties:
                    allowed_address_pairs:
                      - ip_address: $sip.getSharedIpRef()
          - $patchTemplate: $patchTemplate.mergeWith($template)

      - $instanceFipOutput: null
      - If: $floatingIpResourceName != null and $floatingIpNetRef != null
        Then:
          - $instanceFipOutput: $instance.name + '-floatingIPaddress'
          - $template:
              resources:
                $floatingIpResourceName:
                  type: 'OS::Neutron::FloatingIP'
                  properties:
                    floating_network: $floatingIpNetRef
                    port_id:
                      get_resource: $portName
              outputs:
                $instanceFipOutput:
                  value:
                    get_attr: [$floatingIpResourceName, floating_ip_address]
                  description: format('Floating IP of {0}', $instance.name)
          - $instanceResources: $instanceResources.append($floatingIpResourceName)
          - $instanceOutputs: $instanceOutputs.append($instanceFipOutput)
          - $patchTemplate: $patchTemplate.mergeWith($template)
      - Return:
          template: $patchTemplate
          portRef:
            get_resource: $portName
          instanceFipOutput: $instanceFipOutput
          instanceResources: $instanceResources
          instanceOutputs: $instanceOutputs

  generateSecurityGroupManager:
    Body:
      - $region: $.getRegion()
      - $netExplorer: $._getNetExplorer()
      - $securityGroupsEnabled: $netExplorer.listNeutronExtensions().alias.contains('security-group')
      - If: $securityGroupsEnabled
        Then:
          - Return: new(sys:NeutronSecurityGroupManager, $region)
        Else:
          - $._environment.reporter.report($this, "Warning! Security groups are disabled!")
          - Return: new(sys:DummySecurityGroupManager, $region)

  getInstanceIpList:
    Arguments:
      - instance:
          Contract: $.class(Instance).notNull()
    Body:
      - Return: $.getRegion().stack.output()[
          format('addresses-{0}-{1}', $instance.name, id($this))].addr.distinct()
