Provisioning Logic

According to the Resource Model, you should define a new APS type, its relationship with the existing context APS type, and the event processing logic of the respective service.

../../../../_images/event-step-model.png ../../../../_images/event-step-meta.png ../../../../_images/event-step-provision-b.png ../../../../_images/event-step-presentation.png ../../../../_images/event-step-deploy.png ../../../../_images/event-step-provisioning.png

Service Overview

In this project, you have added the events service and now you should define an APS type for the service. A single resource that will be provisioned by that service in the scope of a subscription must have a link with the context resource within the same subscription. This extends the list of APS types in the application.

APS Type

APS ID

Relationship

Attributes

Cloud

http://aps-standard.org/samples/event-mgmt/cloud/1.0

contexts

Collection-Optional

Offer

http://aps-standard.org/samples/event-mgmt/offer/1.0

cloud
vpses
Singular-Required
Collection-Optional

Context

http://aps-standard.org/samples/event-mgmt/context/1.0

cloud
vpses
event
Singular-Required
Collection-Optional
Singular-Optional

VPS

http://aps-standard.org/samples/event-mgmt/vps/1.0

context
offer
user
Singular-Required
Singular-Required
Singular-Required

Event

http://aps-standard.org/samples/event-mgmt/event/1.0

context

Singular-Required

As follows from the table, you do not need to touch clouds.php, offers.php, and vpses.php in this step. The provisioning logic should be modified as follows:

  • In the scripts/contexts.php file, we need to set a relationship with the event type.

  • A new APS type must be defined in the schemas/event.schema file.

  • A new script in the scripts/events.php file must be developed. It must demonstrate how to subscribe to events of different types and process event notifications.

  • For demo purposes, the events service must record event notifications in its internal log and by requests from UI provide the log contents and flush the event log.

Continue Your Demo Project

Continue the demo project from the previous step.

APS Type

In accordance with the Meta Declaration, define the new APS type in the schemas/event.schema file. Its key features must be the following:

  • A required link to the management context APS resource.

  • Subscription to all event types mentioned in the Resource Model section.

Define the new APS type in the schemas/event.schema as follows.

  1. In the emtpy file, create the schema skeleton:

    {
       "apsVersion": "2.0",
       "name": "event",
       "id": "http://aps-standard.org/samples/event-mgmt/event/1.0",
       "implements": [
          "http://aps-standard.org/types/core/resource/1.0"
       ],
       "operations": {
    
       },
       "relations": {
    
       }
    }
    
  2. In the “operations” section, declare event handlers for all event types you are going to receive and process. Each handler definition contains the event type and event source as specified in the APS Events concepts. For example, the definition of a handler triggered by creation of a VPS looks as follows:

    "onVPSavailable": {
       "verb": "POST",
       "path": "/onVPSavailable",
       "eventSubscription": {
           "event" : "http://aps-standard.org/core/events/available",
           "source": {
               "type" : "http://aps-standard.org/samples/event-mgmt/vps/1.0"
           }
       },
       "parameters": {
           "notification": {
               "type": "http://aps-standard.org/types/core/resource/1.0#Notification",
               "required": true,
               "kind": "body"
           }
       }
    },
    
  3. In the “operations” section, declare an auxiliary method called “readNotifications” that will return you all saved event notifications:

    "readNotifications": {
       "verb": "GET",
       "path": "/readNotifications",
       "response": {
           "contentType": null,
           "type": "string",
           "items": null
       }
    },
    
  4. In the “relations” section, declare a required link with the context APS type:

    "relations": {
       "context": {
          "type": "http://aps-standard.org/samples/event-mgmt/context/1.0",
          "required": true,
          "collection": false
       }
    }
    

The complete APS type definition must look as follows:

{
    "apsVersion": "2.0",
    "name": "event",
    "id": "http://aps-standard.org/samples/event-mgmt/event/1.0",
    "implements": [
        "http://aps-standard.org/types/core/resource/1.0"
    ],
    "operations": {
        "readNotifications": {
            "verb": "GET",
            "path": "/readNotifications",
            "response": {
                "contentType": null,
                "type": "string",
                "items": null
            }
        },
        "onVPSavailable": {
            "verb": "POST",
            "path": "/onVPSavailable",
            "eventSubscription": {
                "event" : "http://aps-standard.org/core/events/available",
                "source": {
                    "type" : "http://aps-standard.org/samples/event-mgmt/vps/1.0"
                }
            },
            "parameters": {
                "notification": {
                    "type": "http://aps-standard.org/types/core/resource/1.0#Notification",
                    "required": true,
                    "kind": "body"
                }
            }
        },
        "onVPSremove": {
            "verb": "POST",
            "path": "/onVPSremove",
            "eventSubscription": {
                "event" : "http://aps-standard.org/core/events/removed",
                "source": {
                    "type" : "http://aps-standard.org/samples/event-mgmt/vps/1.0"
                }
            },
            "parameters": {
                "notification": {
                    "type": "http://aps-standard.org/types/core/resource/1.0#Notification",
                    "required": true,
                    "kind": "body"
                }
            }
        },
        "onVPSofferLink": {
            "verb": "POST",
            "path": "/onVPSofferLink",
            "eventSubscription": {
                "event": "http://aps-standard.org/core/events/linked",
                "source": {
                    "type": "http://aps-standard.org/samples/event-mgmt/vps/1.0"
                }
            },
            "parameters": {
                "notification": {
                    "type": "http://aps-standard.org/types/core/resource/1.0#Notification",
                    "required": true,
                    "kind": "body"
                }
            }
        },
        "onVPSofferUnlink": {
            "verb": "POST",
            "path": "/onVPSofferUnlink",
            "eventSubscription": {
                "event": "http://aps-standard.org/core/events/unlinked",
                "source": {
                    "type": "http://aps-standard.org/samples/event-mgmt/vps/1.0"
                }
            },
            "parameters": {
                "notification": {
                    "type": "http://aps-standard.org/types/core/resource/1.0#Notification",
                    "required": true,
                    "kind": "body"
                }
            }
        },
        "onVPSuserLink": {
            "verb": "POST",
            "path": "/onVPSuserLink",
            "eventSubscription": {
                "event": "http://aps-standard.org/core/events/linked",
                "source": {
                    "type": "http://aps-standard.org/samples/event-mgmt/vps/1.0"
                }
            },
          "parameters": {
                "notification": {
                    "type": "http://aps-standard.org/types/core/resource/1.0#Notification",
                    "required": true,
                    "kind": "body"
                }
            }
        },
        "onVPSuserUnlink": {
            "verb": "POST",
            "path": "/onVPSuserUnlink",
            "eventSubscription": {
                "event": "http://aps-standard.org/core/events/unlinked",
                "source": {
                    "type": "http://aps-standard.org/samples/event-mgmt/vps/1.0"
                }
            },
            "parameters": {
                "notification": {
                    "type": "http://aps-standard.org/types/core/resource/1.0#Notification",
                    "required": true,
                    "kind": "body"
                }
            }
        },
        "onVPSchange": {
            "verb": "POST",
            "path": "/onVPSchange",
            "eventSubscription": {
                "event": "http://aps-standard.org/core/events/changed",
                "source": {
                    "type": "http://aps-standard.org/samples/event-mgmt/vps/1.0"
                }
            },
            "parameters": {
                "notification": {
                    "type": "http://aps-standard.org/types/core/resource/1.0#Notification",
                    "required": true,
                    "kind": "body"
                }
            }
        },
        "onDomainAvailable": {
            "verb": "POST",
            "path": "/onDomainAvailable",
            "eventSubscription": {
                "event": "http://aps-standard.org/core/events/available",
                "source": {
                    "type": "http://parallels.com/aps/types/pa/dns/zone/1.0"
                }
            },
            "parameters": {
                "notification": {
                    "type": "http://aps-standard.org/types/core/resource/1.0#Notification",
                    "required": true,
                    "kind": "body"
                }
            }
        },
        "onDomainRemove": {
            "verb": "POST",
            "path": "/onDomainRemove",
            "eventSubscription": {
                "event": "http://aps-standard.org/core/events/removed",
                "source": {
                    "type": "http://parallels.com/aps/types/pa/dns/zone/1.0"
                }
            },
            "parameters": {
                "notification": {
                    "type": "http://aps-standard.org/types/core/resource/1.0#Notification",
                    "required": true,
                    "kind": "body"
                }
            }
        },
        "onUserAvailable": {
            "verb": "POST",
            "path": "/onUserAvailable",
            "eventSubscription": {
                "event": "http://aps-standard.org/core/events/available",
                "source": {
                  "type": "http://parallels.com/aps/types/pa/service-user/1.0"
                }
            },
            "parameters": {
                "notification": {
                    "type": "http://aps-standard.org/types/core/resource/1.0#Notification",
                    "required": true,
                    "kind": "body"
                }
            }
        },
        "onUserRemove": {
            "verb": "POST",
            "path": "/onUserRemove",
            "eventSubscription": {
                "event": "http://aps-standard.org/core/events/removed",
                "source": {
                  "type": "http://parallels.com/aps/types/pa/service-user/1.0"
                }
            },
            "parameters": {
                "notification": {
                    "type": "http://aps-standard.org/types/core/resource/1.0#Notification",
                    "required": true,
                    "kind": "body"
                }
            }
        },
        "onSubscriptionRenew": {
            "verb": "POST",
            "path": "/onSubscriptionRenew",
            "eventSubscription": {
                "event" : "http://parallels.com/aps/events/pa/subscription/renewed",
                "source": {
                    "type" : "http://parallels.com/aps/types/pa/subscription/1.0"
                }
             },
            "parameters": {
                "notification": {
                    "type": "http://aps-standard.org/types/core/resource/1.0#Notification",
                    "required": true,
                    "kind": "body"
                }
            }
        }

    },
    "relations": {
        "context": {
            "type": "http://aps-standard.org/samples/event-mgmt/context/1.0",
            "required": true,
            "collection": false
        }
    }
}

Auxiliary File

We use a simplified event processing - just adding the received notifications to the log file that will be scripts/eventlog.txt by default. A new scripts/logger.php file defines the Logging class with the following public functions:

  • logfile($path) sets the actual path to the log file.

  • logwrite($message) adds a message to the log file.

  • logclose() closes the log file.

  • flushlog() empties the log file.

<?php
 /**
  * Logging class:
  * - contains "logfile", "logwrite", "flushlog", and "logclose" public methods
  * - "logfile" sets path and name of log file
  * - "logwrite" writes message to the log file (and implicitly opens log file)
  * - "logclose" closes log file
  * - "flushlog" returns the event log content and flushes the log
  * - first call of the "logwrite" method will open the log file implicitly
  * - a message is written with the following format: "[d/M/Y:H:i:s] (script name) message"
  */
 class Logging {

     ## Declare log file and file pointer as private properties
     private $log_file = './eventlog.txt';
     private $fp;

     ## Set log file (path and name)
     public function logfile($path) {
         if (strlen($path) > 0) {
                 $this->log_file = $path;
         }
     }

     ## Open log file (private method)
     private function logopen() {
         #### Open log file for writing only and place file pointer at the end of the file.
         #### If the file does not exist, try to create it.
         $this->fp = fopen($this->log_file, 'a+') or exit("Can't open $logfile!");
     }

     ## Close log file (it's always a good idea to close a file when you're done with it)
     public function logclose() {
         fclose($this->fp);
     }

     ## Write message to the log file
     public function logwrite($message) {
         #### If file pointer doesn't exist, then open log file
         if (!is_resource($this->fp)) {
             $this->logopen();
         }
         #### Define script name
         $script_name = pathinfo($_SERVER['PHP_SELF'], PATHINFO_FILENAME);

         #### Define current time and suppress E_WARNING if using the system TZ settings
         #### (don't forget to set the INI setting date.timezone)
         $time = @date('[d/M/Y:H:i:s e]');

         #### Write current time, script name and message to the log file
         fwrite($this->fp, "$time ($script_name) $message\n");
     }

     public function flushlog() {
         #### If the log has not been created, return empty contents
         if (! file_exists($this->log_file))
                 return "";
         $fp = fopen($this->log_file, 'r+');
         if (!$fp) return "";
         $content = file_get_contents($this->log_file);
         ftruncate($fp, 0);
         return $content;
     }
 }
 ?>

Event monitoring logic will use the defined class and its methods to process event notifications.

Server Management Logic

In the scripts/contexts.php, add a link with the event type:

/**
* @link("http://aps-standard.org/samples/event-mgmt/event/1.0")
*/
public $event;

Event Monitoring Logic

In a subscription, there will be one event resource linked with the management context. In the scripts/events.php file, define the methods declared in the event APS type (file schemas/event.schema) as follows:

  1. Create the script structure:

    <?php
    
    # Class Logging  will be used for logging event notifications
     require "logger.php";
     require "aps/2/runtime.php";
    
     /**
     * Class event
     * @type("http://aps-standard.org/samples/event-mgmt/event/1.0")
     * @implements("http://aps-standard.org/types/core/subscription/service/1.0”)
     */
     class event extends \APS\ResourceBase {
    
         ## Define a Logger function that will log a new Event Notification as a message
    
         ## Define a method that returns the list of Notifications logged by the service
    
         ## Define all handlers declared in the APS type
    
     }
     ?>
    
  2. Define a private function Logger() used by event handlers to log event notifications. It calls the Logging class methods defined in the logger.php file:

    function logger($message){
        $requester=$_SERVER['REMOTE_ADDR'];
        $log = new Logging();
        $log->logfile('./'.$this->aps->id.'.log');
        $log->logwrite($requester.":".$message);
        $log->logclose();
    }
    
  3. Define a custom public method readNotifications() that provides the list of event notifications logged by the service and empties the log after that. It uses the Logging class defined in the logger.php file. The method will be called from the custom UI to present a list of logged event notifications on the screen.

    /**
     * @verb(GET)
     * @path("/readNotifications")
     * @return(string)
    */
    public function readNotifications() {
        $log = new Logging();
        $log->logfile('./'.$this->aps->id.'.log');
        return $log->flushlog();
    }
    
  4. For each event handler declared in the event APS type, define a method that stores event notifications in the log. Notifications sent in the message body must comply with the notification structure defined in the abstract APS type Resource. Handlers use the private logger($message) function defined earlier in the same event class.

    • New VPS is available:

      /**
       * @verb(POST)
       * @path("/onVPSavailable")
       * @param("http://aps-standard.org/types/core/resource/1.0#Notification",body)
      */
      public function onVPSavailable($notification) {
          $this->logger("VPS created: ".json_format($notification));
      }
      
    • A VPS is removed:

      /**
       * @verb(POST)
       * @path("/onVPSremove")
       * @param("http://aps-standard.org/types/core/resource/1.0#Notification",body)
      */
      public function onVPSremove($notification) {
          $this->logger("VPS removed: ".json_format($notification));
      }
      
    • A VPS is linked or re-linked to an offer:

      /**
       * @verb(POST)
       * @path("/onVPSofferLink")
       * @param("http://aps-standard.org/types/core/resource/1.0#Notification",body)
      */
      public function onVPSofferLink($notification) {
          $this->logger("New offer Linked: ".json_format($notification));
      }
      
    • A VPS is unlinked from its offer:

      /**
       * @verb(POST)
       * @path("/onVPSofferUnlink")
       * @param("http://aps-standard.org/types/core/resource/1.0#Notification",body)
      */
      public function onVPSofferUnlink($notification) {
          $this->logger("Offer Unlinked: ".json_format($notification));
      }
      
    • A VPS is linked or re-linked to a service user:

      /**
       * @verb(POST)
       * @path("/onVPSuserLink")
       * @param("http://aps-standard.org/types/core/resource/1.0#Notification",body)
      */
      public function onVPSuserLink($notification) {
          $this->logger("New user Linked: ".json_format($notification));
      }
      
    • A VPS is unlinked from its service user:

      /**
       * @verb(POST)
       * @path("/onVPSuserUnlink")
       * @param("http://aps-standard.org/types/core/resource/1.0#Notification",body)
      */
      public function onVPSuserUnlink($notification) {
          $this->logger("User UnLinked: ".json_format($notification));
      }
      
    • VPS properties are changed:

      /**
       * @verb(POST)
       * @path("/onVPSchange")
       * @param("http://aps-standard.org/types/core/resource/1.0#Notification",body)
      */
      public function onVPSchange($notification) {
          $this->logger("VPS properties changed: ".json_format($notification));
      }
      
    • A new hosted domain is available:

      /**
       * @verb(POST)
       * @path("/onDomainAvailable")
       * @param("http://aps-standard.org/types/core/resource/1.0#Notification",body)
      */
      public function onDomainAvailable($notification) {
          $this->logger("New Domain available: ".json_format($notification));
      }
      
    • A hosted domain is removed:

      /**
       * @verb(POST)
       * @path("/onDomainRemove")
       * @param("http://aps-standard.org/types/core/resource/1.0#Notification",body)
      */
      public function onDomainRemove($notification) {
          $this->logger("Domain removed: ".json_format($notification));
      }
      
    • A new service user is available:

      /**
       * @verb(POST)
       * @path("/onUserAvailable")
       * @param("http://aps-standard.org/types/core/resource/1.0#Notification",body)
      */
      public function onUserAvailable($notification) {
          $this->logger("New User available: ".json_format($notification));
      }
      
    • A service user is removed:

      /**
       * @verb(POST)
       * @path("/onUserRemove")
       * @param("http://aps-standard.org/types/core/resource/1.0#Notification",body)
      */
      public function onUserRemove($notification) {
          $this->logger("User removed: ".json_format($notification));
      }
      
    • The subscription providing this event resource is renewed:

      /**
       * @verb(POST)
       * @path("/onSubscriptionRenew")
       * @param("http://aps-standard.org/types/core/resource/1.0#Notification",body)
      */
      public function onSubscriptionRenew($notification) {
         $this->logger("Subscription renewed: ".json_format($notification));
      }
      

Conclusion

You have designed and developed the provisioning logic for the demo application. The new APS type and the respective service are able to subscribe to events and process event notifications in accordance with the scenario.

The files you have created are similar to the respective files in the sample package.