# this catalog bom is an illustration supplying a few useful sample items
# and templates to get started using Brooklyn

brooklyn.catalog:
  bundle: brooklyn-default-catalog
  version: "1.0.0"  # BROOKLYN_VERSION

  # load everything in the classpath with a @Catalog annotation
  scanJavaAnnotations: true
  
  items:

  - id: server
    itemType: entity
    iconUrl: classpath://brooklyn/icons/server.svg
    description: |
      Provision a server, with customizable provisioning.properties and credentials installed, 
      but no other special software process or scripts executed.
    item:
      type: org.apache.brooklyn.entity.software.base.EmptySoftwareProcess
      name: Server
      iconUrl: classpath://brooklyn/icons/server.svg

  - id: vanilla-bash-server
    itemType: entity
    description: |
      Provision a server, with customizable provisioning.properties and credentials installed, 
      but no other special software process or scripts executed.
      The script should be supplied in "launch.command" as per docs on
      org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess.
    item:
      type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
      name: Server with Launch Script (bash)

  - id: load-balancer
    itemType: entity
    description: |
      Create a load balancer which will point at members in the group entity
      referred to by the config key "serverPool". 
      The sensor advertising the port can be configured with the "member.sensor.portNumber" config key,
      defaulting to `http.port`; all member entities which have published "service.up" will then be picked up.
    item:
      type: org.apache.brooklyn.entity.proxy.nginx.NginxController
      name: Load Balancer (nginx)

  - id: cluster
    itemType: entity
    iconUrl: classpath://brooklyn/icons/cluster.svg
    description: |
      Create a cluster of entities, resizable, with starting size "initialSize",
      and using a spec supplied in the "memberSpec" key.
    item:
      type: org.apache.brooklyn.entity.group.DynamicCluster
      iconUrl: classpath://brooklyn/icons/cluster.svg

  - 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 -y 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.
      We recommend using CentOS or Debian as they both provide the required 'riak' package.
    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)
            autoscaler.metricUpperBound: 6000
            autoscaler.metricLowerBound: 3000
            autoscaler.minPoolSize: 3
            autoscaler.maxPoolSize: 8
            autoscaler.resizeUpStabilizationDelay: 30s
            autoscaler.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.
      We recommend using CentOS or Debian as they both provide the required 'riak' package.
    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
            autoscaler.metricUpperBound: 3
            autoscaler.metricLowerBound: 1
            
            # sustain 3 reqs/sec for 2s and it will scale out
            autoscaler.resizeUpStabilizationDelay: 2s
            # only scale down when sustained for 1m
            autoscaler.resizeDownStabilizationDelay: 1m

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

