Application Packaging Standard

Last updated 18-Mar-2019

Provisioning Logic

According to the Resource Model, you should define the 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 event APS type and the events service. The single resource that will be provisioned by the service in a subscription must 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 contexts.php file, we need to set a relationship with the event type.
  • The new 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.

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. Define the APS type and the service (resource factory) in the scripts/events.php file 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 {
    
         ## Require a link to the management context
    
         ## Define a Logger function that will log a new Event Notification as a message
    
         ## Define a function that returns the list of Notifications logged by the service
    
         ## Subscribe to Events during the provisioning process using APS PHP runtime:
         public function provision() {
    
            #### Create several event subscription objects
    
            #### Connect to the APS controller
    
            #### Subscribe to possible events
         }
    
         ## For each type of Event Notifications, define a handler
    
     }
     ?>
    

    In the above code, we are going to re-define the inherited provision() method. It is called only once, when the event resource is provisioned from the subscription. During the provisioning, it will subscribe the resource to various event sources.

  2. Require a link to the management context:

    /**
     * @link("http://aps-standard.org/samples/event-mgmt/context/1.0")
     * @required
    */
    public $context;
    
  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. Define the private Logger() function 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();
    }
    
  5. In the provision() function, create event subscription objects. In each event subscription, specify the event type and event handler.

    • Events on operations with VPSes - creation, removal, change of properties, linking, and unlinking:

      $subVPSavailable = new \APS\EventSubscription(\APS\EventSubscription::Available,
                                                                   "onVPSavailable");
      $subVPSremove = new \APS\EventSubscription(\APS\EventSubscription::Removed,
                                                                   "onVPSremove");
      $subVPSchange = new \APS\EventSubscription(\APS\EventSubscription::Changed,
                                                                   "onVPSchange");
      $subVPSofferLink = new \APS\EventSubscription(\APS\EventSubscription::Linked,
                                                                   "onVPSofferLink");
      $subVPSofferUnlink = new \APS\EventSubscription(\APS\EventSubscription::Unlinked,
                                                                   "onVPSofferUnlink");
      $subVPSuserLink = new \APS\EventSubscription(\APS\EventSubscription::Linked,
                                                                   "onVPSuserLink");
      $subVPSuserUnlink = new \APS\EventSubscription(\APS\EventSubscription::Unlinked,
                                                                   "onVPSuserUnlink");
      
    • Events on creation and removal of customer’s domains:

      $subDomainAvailable = new \APS\EventSubscription(\APS\EventSubscription::Available,
                                                                   "onDomainAvailable");
      $subDomainRemove = new \APS\EventSubscription(\APS\EventSubscription::Removed,
                                                                   "onDomainRemove");
      
    • Events on creation and removal of customer’s service users:

      $subUserAvailable = new \APS\EventSubscription(\APS\EventSubscription::Available,
                                                                   "onUserAvailable");
      $subUserRemove = new \APS\EventSubscription(\APS\EventSubscription::Removed,
                                                                   "onUserRemove");
      
  6. Connect to the APS controller:

    $apsc = \APS\Request::getController();
    if ($apsc == null) {
        error_log("apsc is null");
    }
    
  7. Activate all event subscriptions declared earlier. In each event subscription, specify the event type. For linked and unlinked events, also specify the relation name.

    • Events on operations with VPSes:

      $subVPSavailable->source->type="http://aps-standard.org/samples/event-mgmt/vps/1.0";
      $apsc->subscribe($this, $subVPSavailable);
      $subVPSremove->source->type="http://aps-standard.org/samples/event-mgmt/vps/1.0";
      $apsc->subscribe($this, $subVPSremove);
      $subVPSchange->source->type="http://aps-standard.org/samples/event-mgmt/vps/1.0";
      $apsc->subscribe($this, $subVPSchange);
      $subVPSofferLink->source->type="http://aps-standard.org/samples/event-mgmt/vps/1.0";
      $subVPSofferLink->relation='offer';
      $apsc->subscribe($this, $subVPSofferLink);
      $subVPSofferUnlink->source->type="http://aps-standard.org/samples/event-mgmt/vps/1.0";
      $subVPSofferUnlink->relation='offer';
      $apsc->subscribe($this, $subVPSofferUnlink);
      $subVPSuserLink->source->type="http://aps-standard.org/samples/event-mgmt/vps/1.0";
      $subVPSuserLink->relation='user';
      $apsc->subscribe($this, $subVPSuserLink);
      $subVPSuserUnlink->source->type="http://aps-standard.org/samples/event-mgmt/vps/1.0";
      $subVPSuserUnlink->relation='user';
      $apsc->subscribe($this, $subVPSuserUnlink);
      
    • Events on creation and removal of customer’s domains defined in the platform APS type PAHostedDomain:

      $subDomainAvailable->source->type="http://parallels.com/aps/types/pa/dns/zone/1.0";
      $apsc->subscribe($this, $subDomainAvailable);
      $subDomainRemove->source->type="http://parallels.com/aps/types/pa/dns/zone/1.0";
      $apsc->subscribe($this, $subDomainRemove);
      
    • Events on creation and removal of customer’s service users defined in the platform APS type PAServiceUser:

      $subUserAvailable->source->type="http://parallels.com/aps/types/pa/service-user/1.1";
      $apsc->subscribe($this, $subUserAvailable);
      $subUserRemove->source->type="http://parallels.com/aps/types/pa/service-user/1.1";
      $apsc->subscribe($this, $subUserRemove);
      
  8. For each type of event notifications, define a handler that stores event notifications in the log. Notifications sent in the message body must comply with the notification structure defined in the standard 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));
      }
      

Keynotes:

  • A resource of the event type will requires a singular link with a resource of the context type.
  • The provision() function is substantially extended with the requests for subscribing to various event types. We use three groups of sources: VPSes, domains, and service users. To get subscribed, a number of steps are needed:
    1. Create subscription objects based on the EventSubscription class (defined in APS PHP runtime) with declaration of the event handlers. For example, the event subscription $subVPSofferLink object with the onVPSofferLink handler is created.
    2. Configure the subscription objects. The required parameter is event type, for example, http://aps-standard.org/samples/event-mgmt/vps/1.0. For the linked and unlinked event types, we also define the relation name, for example, offer.
    3. Open connection with the APS controller by means of the getController() function.
    4. Send requests for creating event subscriptions, for example, $apsc->subscribe($this,$subVPSofferLink).
  • Each event handler, declared in an event subscription, is defined as a public function, for example, the onVPSofferLink($notification) function will process POST requests on the “/onVPSofferLink” path. The handler will expect receiving JSON objects in the message body as defined in the http://aps-standard.org/types/core/resource/1.0#Notification structure of the standard APS type Resource. The message processing is pretty simple - notifications are stored in the log file.
  • The script contains the readNotifications() function that provides the log file contents on requests. It flushes the log file after that. The function is needed for integration with the presentation layer.

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.