From a9ebf6e0cb872715d4c7050c03569cf32a3be680 Mon Sep 17 00:00:00 2001 From: Ben Roberts Date: Thu, 18 Mar 2010 02:00:53 +0000 Subject: [PATCH] Core class updates Improved database abstraction class to support insert and select queries in various forms. Added classes for pulling log entries back out of the database. Updated Job class to describe a known job in the database, and added JobStatus to track status updates on jobs. Updated Config class to connect to the database, and load all settings on startup. Improved the class autoloader to support Exceptions in a single file as a special case, and added this file. --- HandBrakeCluster/ClientLogEntry.class.php | 13 +++ HandBrakeCluster/Config.class.php | 49 ++++++++- HandBrakeCluster/Database.class.php | 88 +++++++++++++++- HandBrakeCluster/Exceptions.class.php | 13 +++ HandBrakeCluster/Job.class.php | 116 +++++++++++++++++++- HandBrakeCluster/JobStatus.class.php | 73 +++++++++++++ HandBrakeCluster/Log.class.php | 44 ++++++++ HandBrakeCluster/LogEntry.class.php | 122 ++++++++++++++++++++++ HandBrakeCluster/Main.class.php | 18 +++- HandBrakeCluster/RequestParser.class.php | 1 + HandBrakeCluster/WorkerLogEntry.class.php | 13 +++ 11 files changed, 541 insertions(+), 9 deletions(-) create mode 100644 HandBrakeCluster/ClientLogEntry.class.php create mode 100644 HandBrakeCluster/Exceptions.class.php create mode 100644 HandBrakeCluster/JobStatus.class.php create mode 100644 HandBrakeCluster/LogEntry.class.php create mode 100644 HandBrakeCluster/WorkerLogEntry.class.php diff --git a/HandBrakeCluster/ClientLogEntry.class.php b/HandBrakeCluster/ClientLogEntry.class.php new file mode 100644 index 0000000..9aa16fa --- /dev/null +++ b/HandBrakeCluster/ClientLogEntry.class.php @@ -0,0 +1,13 @@ + diff --git a/HandBrakeCluster/Config.class.php b/HandBrakeCluster/Config.class.php index b4455f9..4218a2a 100644 --- a/HandBrakeCluster/Config.class.php +++ b/HandBrakeCluster/Config.class.php @@ -2,10 +2,53 @@ class HandBrakeCluster_Config { - private $filename; + private $dbconfig; + private $database; - public function __construct($filename) { - $this->filename = $filename; + private $databaseConfig = array(); + private $settings = array(); + + public function __construct($dbconfig) { + $this->dbconfig = $dbconfig; + + $this->parseDatabaseConfig(); + } + + public function parseDatabaseConfig() { + $this->databaseConfig = parse_ini_file($this->dbconfig); + } + + public function getDatabase($key) { + if (!isset($this->databaseConfig[$key])) { + throw new HandBrakeCluster_Exception_DatabaseConfigMissing($key); + } + + return $this->databaseConfig[$key]; + } + + public function setDatabase(HandBrakeCluster_Database $database) { + $this->database = $database; + $this->preload(); + } + + public function preload() { + if (!$this->database) { + throw new HandBrakeCluster_Exception_NoDatabaseConnection(); + } + + $this->settings = $this->database->selectAssoc('SELECT name,value FROM settings', 'name', 'value'); + } + + public function exists($key) { + return isset($this->settings[$key]); + } + + public function get($key) { + if (!isset($this->settings[$key])) { + throw new HandBrakeCluster_Exception_UnknownSetting($key); + } + + return $this->settings[$key]; } }; diff --git a/HandBrakeCluster/Database.class.php b/HandBrakeCluster/Database.class.php index 23945de..5f2075a 100644 --- a/HandBrakeCluster/Database.class.php +++ b/HandBrakeCluster/Database.class.php @@ -3,9 +3,95 @@ class HandBrakeCluster_Database { private $config; + private $dbh; + + private $hostname; + private $username; + private $password; + private $dbname; + + private $prepared_statements = array(); public function __construct(HandBrakeCluster_Config $config) { - $this->config = $config; + $this->config = $config; + + $this->hostname = $this->config->getDatabase('hostname'); + $this->username = $this->config->getDatabase('username'); + $this->password = $this->config->getDatabase('password'); + $this->dbname = $this->config->getDatabase('dbname'); + + try { + $this->dbh = new PDO("mysql:host={$this->hostname};dbname={$this->dbname}", $this->username, $this->password); + } catch (PDOException $e) { + throw new HandBrakeCluster_Exception_DatabaseConnectionFailed($e->getMessage()); + } + + } + + public function __destruct() { + $this->dbh = null; + } + + public function selectAssoc($sql, $key_col, $value_col) { + $results = array(); + + foreach ($this->dbh->query($sql) as $row) { + $results[$row[$key_col]] = $row[$value_col]; + } + + return $results; + } + + public function selectList($sql, $bind_params = null) { + if ($bind_params) { + $stmt = $this->dbh->prepare($sql); + + foreach ($bind_params as $param) { + $stmt->bindValue(':'.$param['name'], $param['value'], $param['type']); + } + + $result = $stmt->execute(); + if (!$result) { + throw new HandBrakeCluster_Exception_DatabaseQueryFailed(); + } + + return $stmt->fetchAll(); + + } else { + $results = array(); + + $result = $this->dbh->query($sql); + foreach ($result as $row) { + $results[] = $row; + } + + return $results; + } + } + + public function selectOne($sql, $bind_params = null) { + $rows = $this->selectList($sql, $bind_params); + if (count($rows) != 1) { + throw new HandBrakeCluster_Exception_ResultCountMismatch(count($rows)); + } + + return $rows[0]; + } + + public function insert($sql, $bind_params = null) { + $stmt = $this->dbh->prepare($sql); + + if ($bind_params) { + foreach ($bind_params as $param) { + $stmt->bindValue(':'.$param['name'], $param['value'], $param['type']); + } + } + + return $stmt->execute(); + } + + public function errorInfo() { + return $this->dbh->errorInfo(); } } diff --git a/HandBrakeCluster/Exceptions.class.php b/HandBrakeCluster/Exceptions.class.php new file mode 100644 index 0000000..6e02620 --- /dev/null +++ b/HandBrakeCluster/Exceptions.class.php @@ -0,0 +1,13 @@ + diff --git a/HandBrakeCluster/Job.class.php b/HandBrakeCluster/Job.class.php index f739033..e10c99f 100644 --- a/HandBrakeCluster/Job.class.php +++ b/HandBrakeCluster/Job.class.php @@ -3,15 +3,127 @@ class HandBrakeCluster_Job { private $id; + private $name; + private $source; + private $destination; + private $title; + private $format; + private $video_codec; + private $video_width; + private $video_height; + private $quantizer; + private $deinterlace; + private $audio_tracks; + private $audio_codecs; + private $audio_names; + private $subtitle_tracks; - public function __construct($id) { - $this->id = $id; + private $statuses = null; + + + public function __construct($id, $name, $source, $destination, $title, $format, $video_codec, $video_width, $video_height, $quantizer, $deinterlace, $audio_tracks, $audio_codecs, $audio_names, $subtitle_tracks) { + $this->id = $id; + $this->name = $name; + $this->source = $source; + $this->destination = $destination; + $this->title = $title; + $this->format = $format; + $this->video_codec = $video_codec; + $this->video_width = $video_width; + $this->video_height = $video_height; + $this->quantizer = $quantizer; + $this->deinterlace = $deinterlace; + $this->audio_tracks = $audio_tracks; + $this->audio_codecs = $audio_codecs; + $this->audio_names = $audio_names; + $this->subtitle_tracks = $subtitle_tracks; + } + + public static function fromDatabaseRow($row) { + return new HandBrakeCluster_Job( + $row['id'], + $row['name'], + $row['source'], + $row['destination'], + $row['title'], + $row['format'], + $row['video_codec'], + $row['video_width'], + $row['video_height'], + $row['quantizer'], + $row['deinterlace'], + $row['audio_tracks'], + $row['audio_codecs'], + $row['audio_names'], + $row['subtitle_tracks'] + ); + } + + public static function fromId($id) { + $database = HandBrakeCluster_Main::instance()->database(); + return HandBrakeCluster_Job::fromDatabaseRow( + $database->selectOne('SELECT * FROM jobs WHERE id=:id', array( + array('name' => 'id', 'value' => $id, 'type' => PDO::PARAM_INT) + ) + ) + ); + } + + public static function all() { + $jobs = array(); + + $database = HandBrakeCluster_Main::instance()->database(); + foreach ($database->selectList('SELECT * FROM jobs') as $row) { + $jobs[] = self::fromDatabaseRow($row); + } + + return $jobs; + } + + public static function allWithStatus($status) { + $jobs = array(); + + $database = HandBrakeCluster_Main::instance()->database(); + foreach ($database->selectList('SELECT * FROM jobs WHERE id IN (SELECT id FROM job_status_current WHERE status=:status)', array( + array('name' => 'status', 'value' => $status, 'type' => PDO::PARAM_INT) + )) as $row) { + $jobs[] = self::fromDatabaseRow($row); + } + + return $jobs; + } + + protected function loadStatuses() { + if ($this->statuses == null) { + $this->statuses = HandBrakeCluster_JobStatus::allForJob($this->id); + } + } + + public function currentStatus() { + $this->loadStatuses(); + return $this->statuses[count($this->statuses) - 1]; } public function id() { return $this->id; } + public function name() { + return $this->name; + } + + public function source() { + return $this->source; + } + + public function destination() { + return $this->destination; + } + + public function title() { + return $this->title; + } + }; ?> diff --git a/HandBrakeCluster/JobStatus.class.php b/HandBrakeCluster/JobStatus.class.php new file mode 100644 index 0000000..29b9adc --- /dev/null +++ b/HandBrakeCluster/JobStatus.class.php @@ -0,0 +1,73 @@ + 'Queued', + self::FAILED => 'Failed', + self::RUNNING => 'Running', + self::COMPLETE => 'Complete' + ); + + protected $id; + protected $job_id; + protected $status; + protected $ctime; + + protected function __construct($id, $job_id, $status, $ctime) { + $this->id = $id; + $this->job_id = $job_id; + $this->status = $status; + $this->ctime = $ctime; + } + + public static function fromDatabaseRow($row) { + return new HandBrakeCluster_JobStatus( + $row['id'], + $row['job_id'], + $row['status'], + $row['ctime'] + ); + } + + public static function allForJob($job_id) { + $statuses = array(); + + $database = HandBrakeCluster_Main::instance()->database(); + foreach ($database->selectList('SELECT * FROM job_status WHERE job_id=:job_id ORDER BY ctime ASC', array( + array('name' => 'job_id', 'value' => $job_id, 'type' => PDO::PARAM_INT), + )) as $row) { + $statuses[] = HandBrakeCluster_JobStatus::fromDatabaseRow($row); + } + + return $statuses; + } + + public function id() { + return $this->id; + } + + public function jobId() { + return $this->job_id; + } + + public function status() { + return $this->status; + } + + public function statusName() { + return self::$status_names[$this->status]; + } + + public function ctime() { + return $this->ctime; + } + +}; + +?> diff --git a/HandBrakeCluster/Log.class.php b/HandBrakeCluster/Log.class.php index 54c8b24..837372c 100644 --- a/HandBrakeCluster/Log.class.php +++ b/HandBrakeCluster/Log.class.php @@ -2,14 +2,58 @@ class HandBrakeCluster_Log { + private static $hostname = ''; + private $database; private $config; public function __construct(HandBrakeCluster_Database $database, HandBrakeCluster_Config $config) { $this->database = $database; $this->config = $config; + + } + + public function log($severity, $message, $job_id = 0) { + $result = $this->database->insert('INSERT INTO client_log (job_id,level,ctime,pid,hostname,progname,line,message) VALUES(:job_id, :level, :ctime, :pid, :hostname, :progname, :line, :message)', + array( + array('name' => 'job_id', 'value' => $job_id, 'type' => PDO::PARAM_INT), + array('name' => 'level', 'value' => $severity, 'type' => PDO::PARAM_STR), + array('name' => 'ctime', 'value' => time(), 'type' => PDO::PARAM_INT), + array('name' => 'pid', 'value' => 0, 'type' => PDO::PARAM_INT), + array('name' => 'hostname', 'value' => self::$hostname, 'type' => PDO::PARAM_STR), + array('name' => 'progname', 'value' => 'webui', 'type' => PDO::PARAM_STR), + array('name' => 'line', 'value' => 0, 'type' => PDO::PARAM_INT), + array('name' => 'message', 'value' => $message, 'type' => PDO::PARAM_STR) + ) + ); + + if (!$result) { + var_dump($this->database->errorInfo()); + } + } + + public function debug($message, $job_id = 0) { + return $this->log('DEBUG', $message, $job_id); + } + + public function info($messgae, $job_id = 0) { + return $this->log('INFO', $message, $job_id); + } + + public function warning($message, $job_id = 0) { + return $this->log('WARNING', $message, $job_id); + } + + public function error($message, $job_id = 0) { + return $this->log('ERROR', $message, $job_id); + } + + public static function initialise() { + self::$hostname = trim(`hostname`); } } +HandBrakeCluster_Log::initialise(); + ?> diff --git a/HandBrakeCluster/LogEntry.class.php b/HandBrakeCluster/LogEntry.class.php new file mode 100644 index 0000000..408399e --- /dev/null +++ b/HandBrakeCluster/LogEntry.class.php @@ -0,0 +1,122 @@ +id = $id; + $this->job_id = $job_id; + $this->level = $level; + $this->ctime = $ctime; + $this->pid = $pid; + $this->hostname = $hostname; + $this->progname = $progname; + $this->line = $line; + $this->message = $message; + } + + public static function fromDatabaseRow($row) { + return new HandBrakeCluster_ClientLogEntry( + $row['id'], + $row['job_id'], + $row['level'], + $row['ctime'], + $row['pid'], + $row['hostname'], + $row['progname'], + $row['line'], + $row['message'] + ); + } + + public static function fromId($id) { + $database = HandBrakeCluster_Main::instance()->database(); + return HandBrakeCluster_ClientLogEntry::fromDatabaseRow( + $database->selectOne('SELECT * FROM '.self::$table_name.' WHERE id=:id', array( + array('name' => 'id', 'value' => $id, 'type' => PDO::PARAM_INT) + ) + ) + ); + } + + public static function recent($limit = 100) { + $entries = array(); + + $database = HandBrakeCluster_Main::instance()->database(); + foreach ($database->selectList('SELECT * FROM '.self::$table_name.' ORDER BY ctime DESC LIMIT :limit', array( + array('name' => 'limit', 'value' => $limit, 'type' => PDO::PARAM_INT) + )) as $row) { + $entries[] = self::fromDatabaseRow($row); + } + + return $entries; + } + + public static function recentForJob($job_id, $limit = 100) { + $entries = array(); + + $database = HandBrakeCluster_Main::instance()->database(); + foreach ($database->selectList('SELECT * FROM '.self::$table_name.' WHERE job_id=:job_id ORDER BY ctime DESC LIMIT :limit', array( + array('name' => 'job_id', 'value' => $job_id, 'type' => PDO::PARAM_INT), + array('name' => 'limit', 'value' => $limit, 'type' => PDO::PARAM_INT) + )) as $row) { + $entries[] = self::fromDatabaseRow($row); + } + + return $entries; + } + + public static function allForNoJob() { + return self::allForJob(0); + } + + public function id() { + return $this->id; + } + + public function jobId() { + return $this->job_id; + } + + public function level() { + return $this->level; + } + + public function ctime() { + return $this->ctime; + } + + public function pid() { + return $this->pid; + } + + public function hostname() { + return $this->hostname; + } + + public function progname() { + return $this->progname; + } + + public function line() { + return $this->line; + } + + public function message() { + return $this->message; + } + +}; + +?> diff --git a/HandBrakeCluster/Main.class.php b/HandBrakeCluster/Main.class.php index e6fbd89..216e5cb 100644 --- a/HandBrakeCluster/Main.class.php +++ b/HandBrakeCluster/Main.class.php @@ -13,8 +13,16 @@ class HandBrakeCluster_Main { private $request; private function __construct() { - $this->smarty = new Smarty(); + $request_string = isset($_GET['l']) ? $_GET['l'] : ''; + $this->config = new HandBrakeCluster_Config("dbconfig.conf"); + $this->database = new HandBrakeCluster_Database($this->config); + $this->config->setDatabase($this->database); + + $this->log = new HandBrakeCluster_Log($this->database, $this->config); + $this->request = new HandBrakeCluster_RequestParser($request_string); + + $this->smarty = new Smarty(); $this->smarty->template_dir = './templates'; $this->smarty->compile_dir = './tmp/templates'; $this->smarty->cache_dir = './tmp/cache'; @@ -23,8 +31,6 @@ class HandBrakeCluster_Main { $this->smarty->assign('version', '0.1'); $this->smarty->assign('base_uri', '/handbrake/'); - $request_string = isset($_GET['l']) ? $_GET['l'] : ''; - $this->request = new HandBrakeCluster_RequestParser($request_string); } public static function instance() { @@ -70,6 +76,12 @@ class HandBrakeCluster_Main { return; } + // Special case: All exceptions are stored in the same file + if (preg_match('/^HandBrakeCluster_Exception_/', $classname)) { + require_once('HandBrakeCluster/Exceptions.class.php'); + return; + } + // Replace any underscores with directory separators $filename = preg_replace('/_/', '/', $classname); diff --git a/HandBrakeCluster/RequestParser.class.php b/HandBrakeCluster/RequestParser.class.php index 3d58a56..24e4f39 100644 --- a/HandBrakeCluster/RequestParser.class.php +++ b/HandBrakeCluster/RequestParser.class.php @@ -39,6 +39,7 @@ class HandBrakeCluster_RequestParser { if (isset($this->vars[$key])) { return $this->vars[$key]; } + return null; } diff --git a/HandBrakeCluster/WorkerLogEntry.class.php b/HandBrakeCluster/WorkerLogEntry.class.php new file mode 100644 index 0000000..bd0fa14 --- /dev/null +++ b/HandBrakeCluster/WorkerLogEntry.class.php @@ -0,0 +1,13 @@ +