Search This Blog

Wednesday, March 27, 2013

Symfony Project sfservermon Post 5 Command Line Operation

This post covers the servers status update process, specifically the operation from the command line.
The previous post discussed managing - (creating, updating and deleting)  the server entities; we are now at the business end - the monitoring of the server status - which can be migrated to Symfony.
The first area to be covered is the access by command line.
The server status is designed to be updated regularly using a cron task. Which requires a command line interface  (CLI).


*/5 * * * * /usr/bin/php /home/phpservermon/webserver/cron/status.cron.php > /var/log/servermon.log 2>&1


The original phpservermon class - status.cron.php is a standard php script application that:

  • Includes config.php
  • Queries the database for active servers
  • Creates a new updater class
  • Loops through each of the servers
  • Assigns the server details and current status to the updater class.
  • Calls the updater getStatus() method to get the current status.
  • Calls the updater notify() method to handle (i.e. send required notifications) of the current status
  • Save the status to the database.

Command Class

This post covers the creation of the updater class and the evocation from the command line.
The symfony console component is used, this post documents the setup and use - in the Acme Demo bundle, in particular 

Command class.


class UpdateCommand extends Command
{
    protected function configure()
    {
        $this
            ->setName('servermon:update')
            ->setDescription('Update server status')
            ->addArgument(
                'serverLabel',
                InputArgument::OPTIONAL,
                'Which server to update?'
            )
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {

$this->container = $this->getApplication()->getKernel()->getContainer();

        $serverLabel= $input->getArgument('serverLabel');

$serverUpdate =  $this->container = $this->getApplication()->getKernel()->getContainer()->get('server_update');

$serverUpdate->update($serverLabel);

    }
}


This is similar to the hello world example - where it takes one optional argument - the server label. It does instantiate a serverUpdate class and calls with any argument (if supplied).

When run from the command line you see:


$ app/console --list
Symfony version 2.1.8-DEV - app/dev/debug
...
servermon
  servermon: update                     Update server status

It is located in a subfolder called Command in the bundle, and the class - ends with Command.
As suggested in the Symfony documentation the command class does little other than check the arguments and calls a dedicated class to perform the task. This is useful as the server update task can also be called from a controller.

Note: the optional argument - the srervername is an addition to the phpservermon original code, I added it when writing the previous post and have kept it as it is useful for testing.

Update (Service) Class.

The Update class - is a perfect candidate for a Symfony service. It needs a connection to the ORM, which is configured in the service file:
resources/config/services.yml


services:
  server_update:
    class: JMPR\ServerMonBundle\Update\ServerUpdate
    arguments: [ @doctrine.orm.entity_manager ]

Dependency injection is used to configure the ORM connection. Fabian has a great introduction blog post on dependency injection (from 2009 but still worth a read). Passed to the class - is the ORM entity manager (@doctrine.orm.entity_manager)

An initial version of the class - which just queries the Server repository to find the matching servers and prints them. As a feature - a server specified on the command line will be checked even if it is not active.

namespace JMPR\ServerMonBundle\Update;

use JMPR\ServerMonBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
class ServerUpdate
{
  protected $em;

  public function __construct($em)
  {
        $this->em = $em;
  }


  protected function configure()
  {
  }

  public function update($serverLabel)
  {

if ($serverLabel) {
  $servers = $this->em->getRepository('JMPRServerMonBundle:MonitorServers')->findByLabel($serverLabel);
     echo "update: <". $serverLabel ."> " . count($servers) ." servers matched\n---";
} else {
  $servers = $this->em->getRepository('JMPRServerMonBundle:MonitorServers')->findByActive('yes');
  echo "update: " . count($servers) ." servers matched\n---";
}

foreach ($servers as $server) {
  echo "server: ". $server->getLabel() . ":" . $server->getIp() . "\n---";
}

    }

It is interesting at this point to compare this to the original phpservermon class: sfUpdate


# classes/sm/smUpdaterStatus.php

class smUpdaterStatus extends smCore {
}

The smCore class, is purely concerned with instantiating a link to the database - the same functionality provided by the Symfony service container:

abstract class smCore {
public $db;

function __construct() {
// add database handler
$this->db = $GLOBALS['db'];
}
}

So the original smCore class was designed to solve the same problem that the dependency injection addresses.
A final note on this topic - the updater class is called from a controller in response to an update request, the route was added:

server_mon_update:
    pattern:  /update
    defaults: { _controller: JMPRServerMonBundle:Default:update }
The base.html.twig template edited:
          <li><a href="{{ path('server_mon_update')}}">update</a></li>
and the controller action:
public function updateAction()
{
$updater = $this->get('server_update');
        $updater->update('');

        return $this->redirect($this->generateUrl('server_mon_homepage'));
}
The get method to the service container will construct an updater class and initialise it.

The next post discusses the ServerUpdate and UpdaterStatus classes in detail.




No comments:

Post a Comment