# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you 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.
#
brooklyn.catalog:
  version: "1.0.0-M1" # BROOKLYN_VERSION

  items:
  - id: 1-server-template
    itemType: template
    name: "Template 1: Server"
    description: |
      Sample YAML to provision a server in a cloud with illustrative VM properties
    item:
      name: Server (Brooklyn Example)

      # this basic example shows how Brooklyn can provision a single raw VM
      # in the cloud or location of your choice

      services:
      - type:           server
        name:           My VM

      # location can be e.g. `softlayer` or `jclouds:openstack-nova:https://9.9.9.9:9999/v2.0/`,
      # or `localhost` or `byon:(hosts="10.9.1.1,10.9.1.2,produser2@10.9.2.{10,11,20-29}")`
      location:
        jclouds:aws-ec2:
          # edit these to use your credential (or delete if credentials specified in brooklyn.properties)
          identity:     <REPLACE>
          credential:   <REPLACE>

          region:       eu-central-1

          # we want Ubuntu, with a lot of RAM
          osFamily:     ubuntu
          minRam:       8gb

          # set up this user and password (default is to authorize a public key)
          user:         sample
          password:     s4mpl3

  - id: 2-bash-web-server-template
    itemType: template
    name: "Template 2: Bash Web Server"
    description: |
      Sample YAML building on Template 1,
      adding bash commands to launch a Python-based web server
      on port 8020
    item:
      name: Python Web Server (Brooklyn Example)

      # this example builds on the previous one,
      # adding some scripts to initialize the VM

      services:
      - type:           vanilla-bash-server
        name:           My Bash Web Server VM
        brooklyn.config:
          install.command: |
            # install python if not present
            which python || \
              { sudo apt-get update && sudo apt-get install python ; } || \
              { sudo yum update && sudo yum install python ; } || \
              { echo WARNING: cannot install python && exit 1 ; }

          customize.command: |
            # create the web page to serve
            cat > index.html << EOF

            Hello world.
            <p>
            I am ${ENTITY_INFO}, ${MESSAGE:-a Brooklyn sample}.
            <p>
            Created at: `date`
            <p>
            I am running at ${HOSTNAME}, with on-box IP configuration:
            <pre>
            `ifconfig | grep inet`
            </pre>

            EOF

          launch.command: |
            # launch in background (ensuring no streams open), and record PID to file
            nohup python -m SimpleHTTPServer ${PORT:-8020} < /dev/null > output.txt 2>&1 &
            echo $! > ${PID_FILE:-pid.txt}
            sleep 5
            ps -p `cat ${PID_FILE:-pid.txt}`
            if [ $? -ne 0 ] ; then
              cat output.txt
              echo WARNING: python web server not running
              exit 1
            fi

          shell.env:
            HOSTNAME:     $brooklyn:attributeWhenReady("host.name")
            PORT:         $brooklyn:config("my.app.port")
            ENTITY_INFO:  $brooklyn:component("this", "")
            MESSAGE:      $brooklyn:config("my.message")

          # custom
          my.app.port:  8020
          my.message:   "good to meet you"

        brooklyn.enrichers:
        # publish the URL as a sensor; the GUI will pick this up (main.uri)
        - type: org.apache.brooklyn.enricher.stock.Transformer
          brooklyn.config:
            uniqueTag: url-generator
            enricher.sourceSensor: host.subnet.hostname
            # use the definition from Attributes class, as it has a RendererHint so GUI makes it a link
            enricher.targetSensor: $brooklyn:sensor("org.apache.brooklyn.core.entity.Attributes", "main.uri")
            enricher.targetValue:
              $brooklyn:formatString:
              - "http://%s:%s/"
              - $brooklyn:attributeWhenReady("host.subnet.hostname")
              - $brooklyn:config("my.app.port")

      location:
        jclouds:aws-ec2:
          region:       eu-central-1
          # edit these (or delete if credentials specified in brooklyn.properties)
          identity:     <REPLACE>
          credential:   <REPLACE>

  - id: 3-bash-web-and-riak-template
    itemType: template
    name: "Template 3: Bash Web Server and Scaling Riak Cluster"
    description: |
      Sample YAML building on Template 2,
      composing that blueprint with a Riak cluster and injecting the URL
    item:
      name: Bash Web Server and Riak Cluster (Brooklyn Example)

      # this example *references* the previous one,
      # combining it with a stock blueprint for a Riak cluster,
      # and shows how a sensor from the latter can be injected

      services:

      # reference template 2, overriding message to point at riak
      - type:           2-bash-web-server-template
        brooklyn.config:
          my.message:   $brooklyn:formatString("connected to Riak at %s",
                            $brooklyn:entity("riak-cluster").attributeWhenReady("main.uri"))
        # and clear the location defined there so it is taken from this template
        locations:      []

      # use the off-the-shelf Riak cluster
      - type:           org.apache.brooklyn.entity.nosql.riak.RiakCluster
        id:             riak-cluster
        initialSize:    3
        # and add a policy to scale based on ops per minute
        brooklyn.policies:
        - type: org.apache.brooklyn.policy.autoscaling.AutoScalerPolicy
          brooklyn.config:
            metric: riak.node.ops.1m.perNode
            # more than 100 ops per second (6k/min) scales out, less than 50 scales back
            # up to a max of 8 riak nodes here (can be changed in GUI / REST API afterwards)
            metricLowerBound: 3000
            metricUpperBound: 6000
            minPoolSize: 3
            maxPoolSize: 8
            resizeUpStabilizationDelay: 30s
            resizeDownStabilizationDelay: 5m

      location:
        jclouds:aws-ec2:
          region:       eu-central-1
          # edit these (or delete if credentials specified in brooklyn.properties)
          identity:     <REPLACE>
          credential:   <REPLACE>

  - id: 4-resilient-bash-web-cluster-template
    itemType: template
    name: "Template 4: Resilient Load-Balanced Bash Web Cluster with Sensors"
    description: |
      Sample YAML to provision a cluster of the bash/python web server nodes,
      with sensors configured, and a load balancer pointing at them,
      and resilience policies for node replacement and scaling
    item:
      name: Resilient Load-Balanced Bash Web Cluster (Brooklyn Example)

      # this final example shows some of the advanced functionality:
      # defining custom sensors, and a cluster with a "spec",
      # policies for resilience and scaling based on that sensor,
      # and wiring a load balancer in front of the cluster

      # combining this with the riak cluster in the previous example
      # is left as a suggested exercise for the user

      services:

      # define a cluster of the web nodes
      - type:           cluster
        name:           Cluster of Bash Web Nodes
        id:             my-web-cluster
        brooklyn.config:
          initialSize:  1
          memberSpec:
            $brooklyn:entitySpec:
              # template 2 is used as the spec for items in this cluster
              # with a new message overwriting the previous,
              # and a lot of sensors defined
              type:           2-bash-web-server-template
              name:           My Bash Web Server VM with Sensors
              # and clear the location defined there so it is taken from this template
              locations:      []

              brooklyn.config:
                my.message:   "part of the cluster"

              brooklyn.initializers:
              # make a simple request-count sensor, by counting the number of 200 responses in output.txt
              - type: org.apache.brooklyn.core.sensor.ssh.SshCommandSensor
                brooklyn.config:
                  name: reqs.count
                  targetType: int
                  period: 5s
                  command: "cat output.txt | grep HTTP | grep 200 | wc | awk '{print $1}'"
              # and publish the port as a sensor so the load-balancer can pick it up
              - type:           org.apache.brooklyn.core.sensor.StaticSensor
                brooklyn.config:
                  name:         app.port
                  targetType:   int
                  static.value: $brooklyn:config("my.app.port")

              brooklyn.enrichers:
              # derive reqs.per_sec from reqs.count
              - type: org.apache.brooklyn.enricher.stock.YamlTimeWeightedDeltaEnricher
                brooklyn.config:
                  enricher.sourceSensor: reqs.count
                  enricher.targetSensor: reqs.per_sec
                  enricher.delta.period: 1s
              # and take an average over 30s for reqs.per_sec into reqs.per_sec.windowed_30s
              - type: org.apache.brooklyn.enricher.stock.YamlRollingTimeWindowMeanEnricher
                brooklyn.config:
                  enricher.sourceSensor: reqs.per_sec
                  enricher.targetSensor: reqs.per_sec.windowed_30s
                  enricher.window.duration: 30s

              # emit failure sensor if a failure connecting to the service is sustained for 30s
              - type: org.apache.brooklyn.policy.ha.ServiceFailureDetector
                brooklyn.config:
                  entityFailed.stabilizationDelay: 30s

              brooklyn.policies:
              # restart if a failure is detected (with a max of one restart in 2m, sensor will propagate otherwise)
              - type: org.apache.brooklyn.policy.ha.ServiceRestarter
                brooklyn.config:
                  failOnRecurringFailuresInThisDuration: 2m

        # back at the cluster, create a total per-sec and some per-node average
        brooklyn.enrichers:
        - type: org.apache.brooklyn.enricher.stock.Aggregator
          brooklyn.config:
            enricher.sourceSensor: reqs.per_sec
            enricher.targetSensor: reqs.per_sec
            transformation: sum
        - type: org.apache.brooklyn.enricher.stock.Aggregator
          brooklyn.config:
            enricher.sourceSensor: reqs.per_sec
            enricher.targetSensor: reqs.per_sec.per_node
            transformation: average
        - type: org.apache.brooklyn.enricher.stock.Aggregator
          brooklyn.config:
            enricher.sourceSensor: reqs.per_sec.windowed_30s
            enricher.targetSensor: reqs.per_sec.windowed_30s.per_node
            transformation: average

        brooklyn.policies:
        # resilience: if a per-node restart policy fails,
        # just throw that node away and create a new one
        - type: org.apache.brooklyn.policy.ha.ServiceReplacer

        # and scale based on reqs/sec
        - type: org.apache.brooklyn.policy.autoscaling.AutoScalerPolicy
          brooklyn.config:
            # scale based on reqs/sec (though in a real-world situation,
            # reqs.per_sec.windowed_30s.per_node might be a better choice)
            metric: reqs.per_sec.per_node

            # really low numbers, so you can trigger a scale-out just by hitting reload a lot
            metricUpperBound: 3
            metricLowerBound: 1

            # sustain 3 reqs/sec for 2s and it will scale out
            resizeUpStabilizationDelay: 2s
            # only scale down when sustained for 1m
            resizeDownStabilizationDelay: 1m

            maxPoolSize: 10

      # and add a load-balancer pointing at the cluster
      - type:           load-balancer
        id:             load-bal
        brooklyn.config:
          # point this load balancer at the cluster, specifying port to forward to
          loadbalancer.serverpool:  $brooklyn:entity("my-web-cluster")
          member.sensor.portNumber: app.port
          # disable sticky sessions to allow easy validation of balancing via browser refresh
          nginx.sticky: false

      brooklyn.enrichers:
      # publish a few useful info sensors and KPI's to the root of the app
      - type: org.apache.brooklyn.enricher.stock.Propagator
        brooklyn.config:
          uniqueTag:    propagate-load-balancer-url
          producer:     $brooklyn:entity("load-bal")
          propagating:
          - main.uri
      - type: org.apache.brooklyn.enricher.stock.Propagator
        brooklyn.config:
          uniqueTag:    propagate-reqs-per-sec
          producer:     $brooklyn:entity("my-web-cluster")
          propagating:
          - reqs.per_sec
          - reqs.per_sec.windowed_30s.per_node

      location:
        jclouds:aws-ec2:
          # edit these (or delete if credentials specified in brooklyn.properties)
          identity:     <REPLACE>
          credential:   <REPLACE>

          region:       eu-central-1
          minRam:       2gb
