From 15c7ef5885e372a421117cbf8addf25d8c682345 Mon Sep 17 00:00:00 2001 From: Duncan Sommerville Date: Fri, 12 Jun 2015 12:31:55 -0400 Subject: [PATCH] Celery backend and support for dev-env worker parallelization --- airtime_mvc/application/models/RabbitMq.php | 21 +- airtime_mvc/application/upgrade/Upgrades.php | 454 +++++------------- airtime_mvc/tests/airtime.conf | 1 + python_apps/airtime-celery/README.rst | 49 ++ .../airtime-celery/celeryconfig.py | 26 +- .../airtime-celery/{uploader.py => tasks.py} | 10 + .../install/conf/airtime-celery | 4 +- .../install/conf/airtime-celery-bananas | 25 + .../install/conf/airtime-celery-cliff | 25 + .../install/conf/airtime-celery-production | 25 + .../install/conf/airtime-celery-staging | 25 + .../install/{upstart => initd}/airtime-celery | 1 + .../install/initd/airtime-celery-bananas | 334 +++++++++++++ .../install/initd/airtime-celery-cliff | 334 +++++++++++++ .../install/initd/airtime-celery-production | 334 +++++++++++++ .../install/initd/airtime-celery-staging | 334 +++++++++++++ python_apps/airtime-celery/setup.py | 30 +- 17 files changed, 1664 insertions(+), 368 deletions(-) create mode 100644 python_apps/airtime-celery/README.rst rename python_apps/airtime-celery/airtime-celery/{uploader.py => tasks.py} (73%) create mode 100644 python_apps/airtime-celery/install/conf/airtime-celery-bananas create mode 100644 python_apps/airtime-celery/install/conf/airtime-celery-cliff create mode 100644 python_apps/airtime-celery/install/conf/airtime-celery-production create mode 100644 python_apps/airtime-celery/install/conf/airtime-celery-staging rename python_apps/airtime-celery/install/{upstart => initd}/airtime-celery (99%) create mode 100644 python_apps/airtime-celery/install/initd/airtime-celery-bananas create mode 100644 python_apps/airtime-celery/install/initd/airtime-celery-cliff create mode 100644 python_apps/airtime-celery/install/initd/airtime-celery-production create mode 100644 python_apps/airtime-celery/install/initd/airtime-celery-staging diff --git a/airtime_mvc/application/models/RabbitMq.php b/airtime_mvc/application/models/RabbitMq.php index da949cd88..0c618861e 100644 --- a/airtime_mvc/application/models/RabbitMq.php +++ b/airtime_mvc/application/models/RabbitMq.php @@ -74,8 +74,7 @@ class Application_Model_RabbitMq $config["rabbitmq"]["port"], false, // Connector true, // Persistent messages - self::$_CELERY_MESSAGE_TIMEOUT, // Result expiration - array()); // SSL opts + self::$_CELERY_MESSAGE_TIMEOUT); // Result expiration } /** @@ -91,7 +90,7 @@ class Application_Model_RabbitMq * @throws CeleryException when no message is found */ public static function sendCeleryMessage($task, $exchange, $data) { - $config = Config::getConfig(); + $config = parse_ini_file($this->_getRmqConfigPath(), true); $queue = $routingKey = $exchange; $c = self::_setupCeleryExchange($config, $exchange, $queue); // Use the exchange name for the queue $result = $c->PostTask($task, $data, true, $routingKey); // and routing key @@ -110,7 +109,7 @@ class Application_Model_RabbitMq * @throws CeleryException when no message is found */ public static function getAsyncResultMessage($task, $id) { - $config = Config::getConfig(); + $config = parse_ini_file($this->_getRmqConfigPath(), true); $queue = self::$_CELERY_RESULTS_EXCHANGE . "." . $config["stationId"]; $c = self::_setupCeleryExchange($config, self::$_CELERY_RESULTS_EXCHANGE, $queue); $message = $c->getAsyncResultMessage($task, $id); @@ -158,12 +157,10 @@ class Application_Model_RabbitMq self::sendMessage($exchange, 'direct', true, $data); } - public static function SendMessageToAnalyzer($tmpFilePath, $importedStorageDirectory, $originalFilename, - $callbackUrl, $apiKey, $storageBackend, $filePrefix) - { + private function _getRmqConfigPath() { //Hack for Airtime Pro. The RabbitMQ settings for communicating with airtime_analyzer are global //and shared between all instances on Airtime Pro. - $CC_CONFIG = Config::getConfig(); + $CC_CONFIG = Config::getConfig(); $devEnv = "production"; //Default if (array_key_exists("dev_env", $CC_CONFIG)) { $devEnv = $CC_CONFIG["dev_env"]; @@ -174,7 +171,13 @@ class Application_Model_RabbitMq // to the production rabbitmq-analyzer.ini $rmq_config_path = "/etc/airtime-saas/production/rabbitmq-analyzer.ini"; } - $config = parse_ini_file($rmq_config_path, true); + return $rmq_config_path; + } + + public static function SendMessageToAnalyzer($tmpFilePath, $importedStorageDirectory, $originalFilename, + $callbackUrl, $apiKey, $storageBackend, $filePrefix) + { + $config = parse_ini_file($this->_getRmqConfigPath(), true); $conn = new AMQPConnection($config["rabbitmq"]["host"], $config["rabbitmq"]["port"], $config["rabbitmq"]["user"], diff --git a/airtime_mvc/application/upgrade/Upgrades.php b/airtime_mvc/application/upgrade/Upgrades.php index d5eb4e7c2..21cad0e71 100644 --- a/airtime_mvc/application/upgrade/Upgrades.php +++ b/airtime_mvc/application/upgrade/Upgrades.php @@ -44,42 +44,53 @@ class UpgradeManager */ } + /** + * Upgrade the Airtime schema version to match the highest supported version + * + * @return boolean whether or not an upgrade was performed + */ public function doUpgrade() { // Get all upgrades dynamically (in declaration order!) so we don't have to add them explicitly each time // TODO: explicitly sort classnames by ascending version suffix for safety $upgraders = getUpgrades(); - return $this->runUpgrades($upgraders, (dirname(__DIR__) . "/controllers")); + $dir = (dirname(__DIR__) . "/controllers"); + $upgradePerformed = false; + + foreach ($upgraders as $upgrader) { + $upgradePerformed = $this->_runUpgrade(new $upgrader($dir)) ? true : $upgradePerformed; + } + + return $upgradePerformed; } /** - * Run a given set of upgrades - * - * @param array $upgraders the upgrades to perform - * @param string $dir the directory containing the upgrade sql - * @return boolean whether or not an upgrade was performed + * Run the given upgrade + * + * @param $upgrader AirtimeUpgrader the upgrade class to be executed + * + * @return bool true if the upgrade was successful, otherwise false */ - public function runUpgrades($upgraders, $dir) { - $upgradePerformed = false; - foreach ($upgraders as $upgrader) { - /** @var $upgrader AirtimeUpgrader */ - $upgrader = new $upgrader(); - if ($upgrader->checkIfUpgradeSupported()) - { - // pass the given directory to the upgrades, since __DIR__ returns parent dir of file, not executor - $upgrader->upgrade($dir); //This will throw an exception if the upgrade fails. - $upgradePerformed = true; - } - } - return $upgradePerformed; + private function _runUpgrade(AirtimeUpgrader $upgrader) { + return $upgrader->checkIfUpgradeSupported() && $upgrader->upgrade(); } } abstract class AirtimeUpgrader { + protected $_dir; + + /** + * @param $dir string directory housing upgrade files + */ + public function __construct($dir) { + $this->_dir = $dir; + } + /** Schema versions that this upgrader class can upgrade from (an array of version strings). */ abstract protected function getSupportedSchemaVersions(); + /** The schema version that this upgrader class will upgrade to. (returns a version string) */ abstract public function getNewVersion(); @@ -94,10 +105,7 @@ abstract class AirtimeUpgrader */ public function checkIfUpgradeSupported() { - if (!in_array(AirtimeUpgrader::getCurrentSchemaVersion(), $this->getSupportedSchemaVersions())) { - return false; - } - return true; + return in_array(AirtimeUpgrader::getCurrentSchemaVersion(), $this->getSupportedSchemaVersions()); } protected function toggleMaintenanceScreen($toggle) @@ -122,8 +130,45 @@ abstract class AirtimeUpgrader } } - /** Implement this for each new version of Airtime */ - abstract public function upgrade(); + /** + * Implement this for each new version of Airtime + * This function abstracts out the core upgrade functionality, + * allowing child classes to overwrite _runUpgrade to reduce duplication + */ + public function upgrade() { + Cache::clear(); + assert($this->checkIfUpgradeSupported()); + + try { + // $this->toggleMaintenanceScreen(true); + Cache::clear(); + + $this->_runUpgrade(); + + Application_Model_Preference::SetSchemaVersion($this->getNewVersion()); + Cache::clear(); + + // $this->toggleMaintenanceScreen(false); + } catch(Exception $e) { + // $this->toggleMaintenanceScreen(false); + return false; + } + + return true; + } + + protected function _runUpgrade() { + $airtimeConf = isset($_SERVER['AIRTIME_CONF']) ? $_SERVER['AIRTIME_CONF'] : "/etc/airtime/airtime.conf"; + $values = parse_ini_file($airtimeConf, true); + + $username = $values['database']['dbuser']; + $password = $values['database']['dbpass']; + $host = $values['database']['host']; + $database = $values['database']['dbname']; + + passthru("export PGPASSWORD=$password && psql -h $host -U $username -q -f ".$this->_dir."/upgrade_sql/airtime_" + .$this->getNewVersion()."/upgrade.sql $database 2>&1 | grep -v -E \"will create implicit sequence|will create implicit index\""); + } } class AirtimeUpgrader253 extends AirtimeUpgrader @@ -137,60 +182,17 @@ class AirtimeUpgrader253 extends AirtimeUpgrader { return '2.5.3'; } - - public function upgrade($dir = __DIR__) - { - Cache::clear(); - assert($this->checkIfUpgradeSupported()); - - $con = Propel::getConnection(); - $con->beginTransaction(); - try { - - $this->toggleMaintenanceScreen(true); - Cache::clear(); - - //Begin upgrade - - //Update disk_usage value in cc_pref - $musicDir = CcMusicDirsQuery::create() - ->filterByType('stor') - ->filterByExists(true) - ->findOne(); - $storPath = $musicDir->getDirectory(); - - //Update disk_usage value in cc_pref - $storDir = isset($_SERVER['AIRTIME_BASE']) ? $_SERVER['AIRTIME_BASE']."srv/airtime/stor" : "/srv/airtime/stor"; - $diskUsage = shell_exec("du -sb $storDir | awk '{print $1}'"); - - Application_Model_Preference::setDiskUsage($diskUsage); - - //clear out the cache - Cache::clear(); - - $con->commit(); - - //update system_version in cc_pref and change some columns in cc_files - $airtimeConf = isset($_SERVER['AIRTIME_CONF']) ? $_SERVER['AIRTIME_CONF'] : "/etc/airtime/airtime.conf"; - $values = parse_ini_file($airtimeConf, true); - - $username = $values['database']['dbuser']; - $password = $values['database']['dbpass']; - $host = $values['database']['host']; - $database = $values['database']['dbname']; - - passthru("export PGPASSWORD=$password && psql -h $host -U $username -q -f $dir/upgrade_sql/airtime_".$this->getNewVersion()."/upgrade.sql $database 2>&1 | grep -v -E \"will create implicit sequence|will create implicit index\""); - Application_Model_Preference::SetSchemaVersion($this->getNewVersion()); - //clear out the cache - Cache::clear(); - - $this->toggleMaintenanceScreen(false); - - } catch (Exception $e) { - $con->rollback(); - $this->toggleMaintenanceScreen(false); - } + protected function _runUpgrade() + { + //Update disk_usage value in cc_pref + $storDir = isset($_SERVER['AIRTIME_BASE']) ? $_SERVER['AIRTIME_BASE']."srv/airtime/stor" : "/srv/airtime/stor"; + $diskUsage = shell_exec("du -sb $storDir | awk '{print $1}'"); + + Application_Model_Preference::setDiskUsage($diskUsage); + + //update system_version in cc_pref and change some columns in cc_files + parent::_runUpgrade(); } } @@ -205,78 +207,49 @@ class AirtimeUpgrader254 extends AirtimeUpgrader return '2.5.4'; } - public function upgrade() + protected function _runUpgrade() { - Cache::clear(); - - assert($this->checkIfUpgradeSupported()); - - $newVersion = $this->getNewVersion(); - - $con = Propel::getConnection(); - //$con->beginTransaction(); - try { - $this->toggleMaintenanceScreen(true); - Cache::clear(); - - //Begin upgrade - - //First, ensure there are no superadmins already. - $numberOfSuperAdmins = CcSubjsQuery::create() + //First, ensure there are no superadmins already. + $numberOfSuperAdmins = CcSubjsQuery::create() ->filterByDbType(UTYPE_SUPERADMIN) ->filterByDbLogin("sourcefabric_admin", Criteria::NOT_EQUAL) //Ignore sourcefabric_admin users ->count(); - - //Only create a super admin if there isn't one already. - if ($numberOfSuperAdmins == 0) - { - //Find the "admin" user and promote them to superadmin. - $adminUser = CcSubjsQuery::create() + + //Only create a super admin if there isn't one already. + if ($numberOfSuperAdmins == 0) + { + //Find the "admin" user and promote them to superadmin. + $adminUser = CcSubjsQuery::create() ->filterByDbLogin('admin') ->findOne(); - if (!$adminUser) - { - //TODO: Otherwise get the user with the lowest ID that is of type administrator: - // - $adminUser = CcSubjsQuery::create() + if (!$adminUser) + { + // Otherwise get the user with the lowest ID that is of type administrator: + $adminUser = CcSubjsQuery::create() ->filterByDbType(UTYPE_ADMIN) ->orderByDbId(Criteria::ASC) ->findOne(); - - if (!$adminUser) { - throw new Exception("Failed to find any users of type 'admin' ('A')."); - } - } - - $adminUser = new Application_Model_User($adminUser->getDbId()); - $adminUser->setType(UTYPE_SUPERADMIN); - $adminUser->save(); - Logging::info($_SERVER['HTTP_HOST'] . ': ' . $newVersion . " Upgrade: Promoted user " . $adminUser->getLogin() . " to be a Super Admin."); - - //Also try to promote the sourcefabric_admin user - $sofabAdminUser = CcSubjsQuery::create() - ->filterByDbLogin('sourcefabric_admin') - ->findOne(); - if ($sofabAdminUser) { - $sofabAdminUser = new Application_Model_User($sofabAdminUser->getDbId()); - $sofabAdminUser->setType(UTYPE_SUPERADMIN); - $sofabAdminUser->save(); - Logging::info($_SERVER['HTTP_HOST'] . ': ' . $newVersion . " Upgrade: Promoted user " . $sofabAdminUser->getLogin() . " to be a Super Admin."); + + if (!$adminUser) { + throw new Exception("Failed to find any users of type 'admin' ('A')."); } } - - //$con->commit(); - Application_Model_Preference::SetSchemaVersion($newVersion); - Cache::clear(); - - $this->toggleMaintenanceScreen(false); - - return true; - - } catch(Exception $e) { - //$con->rollback(); - $this->toggleMaintenanceScreen(false); - throw $e; + + $adminUser = new Application_Model_User($adminUser->getDbId()); + $adminUser->setType(UTYPE_SUPERADMIN); + $adminUser->save(); + Logging::info($_SERVER['HTTP_HOST'] . ': ' . $this->getNewVersion() . " Upgrade: Promoted user " . $adminUser->getLogin() . " to be a Super Admin."); + + //Also try to promote the sourcefabric_admin user + $sofabAdminUser = CcSubjsQuery::create() + ->filterByDbLogin('sourcefabric_admin') + ->findOne(); + if ($sofabAdminUser) { + $sofabAdminUser = new Application_Model_User($sofabAdminUser->getDbId()); + $sofabAdminUser->setType(UTYPE_SUPERADMIN); + $sofabAdminUser->save(); + Logging::info($_SERVER['HTTP_HOST'] . ': ' . $this->getNewVersion() . " Upgrade: Promoted user " . $sofabAdminUser->getLogin() . " to be a Super Admin."); + } } } } @@ -291,40 +264,6 @@ class AirtimeUpgrader255 extends AirtimeUpgrader { public function getNewVersion() { return '2.5.5'; } - - public function upgrade($dir = __DIR__) { - Cache::clear(); - assert($this->checkIfUpgradeSupported()); - - $newVersion = $this->getNewVersion(); - - try { - $this->toggleMaintenanceScreen(true); - Cache::clear(); - - // Begin upgrade - $airtimeConf = isset($_SERVER['AIRTIME_CONF']) ? $_SERVER['AIRTIME_CONF'] : "/etc/airtime/airtime.conf"; - $values = parse_ini_file($airtimeConf, true); - - $username = $values['database']['dbuser']; - $password = $values['database']['dbpass']; - $host = $values['database']['host']; - $database = $values['database']['dbname']; - - passthru("export PGPASSWORD=$password && psql -h $host -U $username -q -f $dir/upgrade_sql/airtime_" - .$this->getNewVersion()."/upgrade.sql $database 2>&1 | grep -v -E \"will create implicit sequence|will create implicit index\""); - - Application_Model_Preference::SetSchemaVersion($newVersion); - Cache::clear(); - - $this->toggleMaintenanceScreen(false); - - return true; - } catch(Exception $e) { - $this->toggleMaintenanceScreen(false); - throw $e; - } - } } class AirtimeUpgrader259 extends AirtimeUpgrader { @@ -337,38 +276,6 @@ class AirtimeUpgrader259 extends AirtimeUpgrader { public function getNewVersion() { return '2.5.9'; } - - public function upgrade($dir = __DIR__) { - Cache::clear(); - assert($this->checkIfUpgradeSupported()); - - $newVersion = $this->getNewVersion(); - - try { - $this->toggleMaintenanceScreen(true); - Cache::clear(); - - // Begin upgrade - $airtimeConf = isset($_SERVER['AIRTIME_CONF']) ? $_SERVER['AIRTIME_CONF'] : "/etc/airtime/airtime.conf"; - $values = parse_ini_file($airtimeConf, true); - - $username = $values['database']['dbuser']; - $password = $values['database']['dbpass']; - $host = $values['database']['host']; - $database = $values['database']['dbname']; - - passthru("export PGPASSWORD=$password && psql -h $host -U $username -q -f $dir/upgrade_sql/airtime_" - .$this->getNewVersion()."/upgrade.sql $database 2>&1 | grep -v -E \"will create implicit sequence|will create implicit index\""); - - Application_Model_Preference::SetSchemaVersion($newVersion); - Cache::clear(); - - $this->toggleMaintenanceScreen(false); - } catch(Exception $e) { - $this->toggleMaintenanceScreen(false); - throw $e; - } - } } class AirtimeUpgrader2510 extends AirtimeUpgrader @@ -382,38 +289,6 @@ class AirtimeUpgrader2510 extends AirtimeUpgrader public function getNewVersion() { return '2.5.10'; } - - public function upgrade($dir = __DIR__) { - Cache::clear(); - assert($this->checkIfUpgradeSupported()); - - $newVersion = $this->getNewVersion(); - - try { - $this->toggleMaintenanceScreen(true); - Cache::clear(); - - // Begin upgrade - $airtimeConf = isset($_SERVER['AIRTIME_CONF']) ? $_SERVER['AIRTIME_CONF'] : "/etc/airtime/airtime.conf"; - $values = parse_ini_file($airtimeConf, true); - - $username = $values['database']['dbuser']; - $password = $values['database']['dbpass']; - $host = $values['database']['host']; - $database = $values['database']['dbname']; - - passthru("export PGPASSWORD=$password && psql -h $host -U $username -q -f $dir/upgrade_sql/airtime_" - .$this->getNewVersion()."/upgrade.sql $database 2>&1 | grep -v -E \"will create implicit sequence|will create implicit index\""); - - Application_Model_Preference::SetSchemaVersion($newVersion); - Cache::clear(); - - $this->toggleMaintenanceScreen(false); - } catch(Exception $e) { - $this->toggleMaintenanceScreen(false); - throw $e; - } - } } class AirtimeUpgrader2511 extends AirtimeUpgrader @@ -428,35 +303,13 @@ class AirtimeUpgrader2511 extends AirtimeUpgrader return '2.5.11'; } - public function upgrade($dir = __DIR__) { - Cache::clear(); - assert($this->checkIfUpgradeSupported()); - - $newVersion = $this->getNewVersion(); - - try { - $this->toggleMaintenanceScreen(true); - Cache::clear(); - - // Begin upgrade - $queryResult = CcFilesQuery::create() - ->select(array('disk_usage')) - ->withColumn('SUM(CcFiles.filesize)', 'disk_usage') - ->find(); - $disk_usage = $queryResult[0]; - Application_Model_Preference::setDiskUsage($disk_usage); - - Application_Model_Preference::SetSchemaVersion($newVersion); - Cache::clear(); - - $this->toggleMaintenanceScreen(false); - } catch(Exception $e) { - $this->toggleMaintenanceScreen(false); - throw $e; - } - } - public function downgrade() { - + protected function _runUpgrade() { + $queryResult = CcFilesQuery::create() + ->select(array('disk_usage')) + ->withColumn('SUM(CcFiles.filesize)', 'disk_usage') + ->find(); + $disk_usage = $queryResult[0]; + Application_Model_Preference::setDiskUsage($disk_usage); } } @@ -472,41 +325,6 @@ class AirtimeUpgrader2512 extends AirtimeUpgrader public function getNewVersion() { return '2.5.12'; } - - public function upgrade($dir = __DIR__) { - Cache::clear(); - assert($this->checkIfUpgradeSupported()); - - $newVersion = $this->getNewVersion(); - - try { - $this->toggleMaintenanceScreen(true); - Cache::clear(); - - // Begin upgrade - $airtimeConf = isset($_SERVER['AIRTIME_CONF']) ? $_SERVER['AIRTIME_CONF'] : "/etc/airtime/airtime.conf"; - $values = parse_ini_file($airtimeConf, true); - - $username = $values['database']['dbuser']; - $password = $values['database']['dbpass']; - $host = $values['database']['host']; - $database = $values['database']['dbname']; - - passthru("export PGPASSWORD=$password && psql -h $host -U $username -q -f $dir/upgrade_sql/airtime_" - .$this->getNewVersion()."/upgrade.sql $database 2>&1 | grep -v -E \"will create implicit sequence|will create implicit index\""); - - Application_Model_Preference::SetSchemaVersion($newVersion); - Cache::clear(); - - $this->toggleMaintenanceScreen(false); - } catch(Exception $e) { - $this->toggleMaintenanceScreen(false); - throw $e; - } - } - public function downgrade() { - - } } class AirtimeUpgrader2513 extends AirtimeUpgrader @@ -520,38 +338,4 @@ class AirtimeUpgrader2513 extends AirtimeUpgrader public function getNewVersion() { return '2.5.13'; } - - public function upgrade($dir = __DIR__) { - Cache::clear(); - assert($this->checkIfUpgradeSupported()); - - $newVersion = $this->getNewVersion(); - - try { - $this->toggleMaintenanceScreen(true); - Cache::clear(); - - // Begin upgrade - $airtimeConf = isset($_SERVER['AIRTIME_CONF']) ? $_SERVER['AIRTIME_CONF'] : "/etc/airtime/airtime.conf"; - $values = parse_ini_file($airtimeConf, true); - - $username = $values['database']['dbuser']; - $password = $values['database']['dbpass']; - $host = $values['database']['host']; - $database = $values['database']['dbname']; - - passthru("export PGPASSWORD=$password && psql -h $host -U $username -q -f $dir/upgrade_sql/airtime_" - .$newVersion."/upgrade.sql $database 2>&1 | grep -v -E \"will create implicit sequence|will create implicit index\""); - - Application_Model_Preference::SetSchemaVersion($newVersion); - Cache::clear(); - - $this->toggleMaintenanceScreen(false); - } catch(Exception $e) { - $this->toggleMaintenanceScreen(false); - throw $e; - } - } - } - diff --git a/airtime_mvc/tests/airtime.conf b/airtime_mvc/tests/airtime.conf index fb19bcaa0..431d3f2c2 100644 --- a/airtime_mvc/tests/airtime.conf +++ b/airtime_mvc/tests/airtime.conf @@ -19,6 +19,7 @@ base_url = localhost base_port = 80 base_dir = / cache_ahead_hours = 1 +station_id = teststation [monit] monit_user = guest diff --git a/python_apps/airtime-celery/README.rst b/python_apps/airtime-celery/README.rst new file mode 100644 index 000000000..7de00cf7b --- /dev/null +++ b/python_apps/airtime-celery/README.rst @@ -0,0 +1,49 @@ +airtime-celery +============== + +airtime-celery is a Celery_ daemon for handling backend tasks asynchronously. +Communication and the Celery results backend are both handled with amqp (RabbitMQ). + +Installation +============ + + $ sudo python setup.py install + +To install the configuration and upstart files for all environments (development and production) + + $ sudo python setup.py install --all-envs + +You can also specify a single environment to deploy config and upstart files for: + + $ sudo python setup.py install --dev-env=bananas + +Each instance of airtime-celery has its own worker, and multiple instances can be run in parallel. +`Celery is thread-safe`_, so this parallelization won't cause conflicts. + +.. _Celery: http://www.celeryproject.org/ +.. _Celery is thread-safe: http://celery.readthedocs.org/en/latest/userguide/application.html + +Usage +===== + +This program must be run with sudo: + + $ sudo service airtime-celery {start | stop | restart | graceful | kill | dryrun | create-paths} + +Developers +========== + +You may want to use the setuptools develop target to install: + + $ sudo python setup.py develop + +You will need to allow the "airtime" RabbitMQ user to access all exchanges and queues within the /airtime vhost: + + $ sudo rabbitmqctl set_permissions -p /airtime airtime .\* .\* .\* + +Logging +======= + +By default, logs are saved to: + + /var/log/airtime/airtime-celery[-DEV_ENV].log diff --git a/python_apps/airtime-celery/airtime-celery/celeryconfig.py b/python_apps/airtime-celery/airtime-celery/celeryconfig.py index fcdd83a70..cb986d0f9 100644 --- a/python_apps/airtime-celery/airtime-celery/celeryconfig.py +++ b/python_apps/airtime-celery/airtime-celery/celeryconfig.py @@ -1,26 +1,26 @@ +import os from configobj import ConfigObj from kombu import Exchange, Queue # Get the broker string from airtime.conf -DEFAULT_RMQ_CONFIG_PATH = '/etc/airtime/airtime.conf' RMQ_CONFIG_SECTION = "rabbitmq" +def get_rmq_broker(): + rmq_config = ConfigObj(os.environ['RMQ_CONFIG_FILE']) + rmq_settings = parse_rmq_config(rmq_config) + return 'amqp://{username}:{password}@{host}:{port}/{vhost}'.format(**rmq_settings) + + def parse_rmq_config(rmq_config): return { - 'host' : rmq_config[RMQ_CONFIG_SECTION]['host'], - 'port' : rmq_config[RMQ_CONFIG_SECTION]['port'], - 'username' : rmq_config[RMQ_CONFIG_SECTION]['user'], - 'password' : rmq_config[RMQ_CONFIG_SECTION]['password'], - 'vhost' : rmq_config[RMQ_CONFIG_SECTION]['vhost'] + 'host' : rmq_config[RMQ_CONFIG_SECTION]['host'], + 'port' : rmq_config[RMQ_CONFIG_SECTION]['port'], + 'username': rmq_config[RMQ_CONFIG_SECTION]['user'], + 'password': rmq_config[RMQ_CONFIG_SECTION]['password'], + 'vhost' : rmq_config[RMQ_CONFIG_SECTION]['vhost'] } - -def get_rmq_broker(): - rmq_config = ConfigObj(DEFAULT_RMQ_CONFIG_PATH) - rmq_settings = parse_rmq_config(rmq_config) - return 'amqp://{username}:{password}@{host}:{port}/{vhost}'.format(**rmq_settings) - # Celery amqp settings BROKER_URL = get_rmq_broker() CELERY_RESULT_BACKEND = 'amqp' # Use RabbitMQ as the celery backend @@ -34,7 +34,7 @@ CELERY_QUEUES = ( ) CELERY_ROUTES = ( { - 'soundcloud_uploads.uploader.upload_to_soundcloud': { + 'soundcloud_uploads.tasks.upload_to_soundcloud': { 'exchange': 'airtime-results', 'queue': 'airtime-results.soundcloud-uploads', } diff --git a/python_apps/airtime-celery/airtime-celery/uploader.py b/python_apps/airtime-celery/airtime-celery/tasks.py similarity index 73% rename from python_apps/airtime-celery/airtime-celery/uploader.py rename to python_apps/airtime-celery/airtime-celery/tasks.py index addbc964d..76009a75f 100644 --- a/python_apps/airtime-celery/airtime-celery/uploader.py +++ b/python_apps/airtime-celery/airtime-celery/tasks.py @@ -11,6 +11,16 @@ logger = get_task_logger(__name__) @celery.task(name='upload-to-soundcloud') def upload_to_soundcloud(data, token, file_path): + """ + Upload a file to SoundCloud + + :param data: associative array containing SoundCloud metadata + :param token: OAuth2 client access token + :param file_path: path to the file being uploaded + + :return: the SoundCloud response object + :rtype: dict + """ client = soundcloud.Client(access_token=token) # Open the file with urllib2 if it's a cloud file data['asset_data'] = open(file_path, 'rb') if os.path.isfile(file_path) else urllib2.urlopen(file_path) diff --git a/python_apps/airtime-celery/install/conf/airtime-celery b/python_apps/airtime-celery/install/conf/airtime-celery index b026a8069..3b1206738 100644 --- a/python_apps/airtime-celery/install/conf/airtime-celery +++ b/python_apps/airtime-celery/install/conf/airtime-celery @@ -5,10 +5,10 @@ CELERYD_NODES="airtime-celery" CELERY_BIN="/usr/local/bin/celery" # App instance to use -CELERY_APP="airtime-celery.uploader:celery" +CELERY_APP="airtime-celery.tasks:celery" # Extra command-line arguments to the worker -CELERYD_OPTS="--time-limit=300 --concurrency=8 --config=celeryconfig" +CELERYD_OPTS="--time-limit=300 --concurrency=1 --config=celeryconfig" # %N will be replaced with the first part of the nodename. CELERYD_LOG_FILE="/var/log/airtime/%N.log" diff --git a/python_apps/airtime-celery/install/conf/airtime-celery-bananas b/python_apps/airtime-celery/install/conf/airtime-celery-bananas new file mode 100644 index 000000000..15cd5d8b3 --- /dev/null +++ b/python_apps/airtime-celery/install/conf/airtime-celery-bananas @@ -0,0 +1,25 @@ +# Names of nodes to start +CELERYD_NODES="airtime-celery-bananas" + +# Absolute or relative path to the 'celery' command: +CELERY_BIN="/usr/local/bin/celery" + +# App instance to use +CELERY_APP="airtime-celery.tasks:celery" + +# Extra command-line arguments to the worker +CELERYD_OPTS="--time-limit=300 --concurrency=1 --config=celeryconfig" + +# %N will be replaced with the first part of the nodename. +CELERYD_LOG_FILE="/var/log/airtime/%N.log" +CELERYD_PID_FILE="/var/run/celery/%N.pid" + +# Workers should run as an unprivileged user. +# You need to create this user manually (or you can choose +# a user/group combination that already exists, e.g. nobody). +CELERYD_USER="celery" +CELERYD_GROUP="celery" + +# If enabled pid and log directories will be created if missing, +# and owned by the userid/group configured. +CELERY_CREATE_DIRS=1 diff --git a/python_apps/airtime-celery/install/conf/airtime-celery-cliff b/python_apps/airtime-celery/install/conf/airtime-celery-cliff new file mode 100644 index 000000000..a08e14bde --- /dev/null +++ b/python_apps/airtime-celery/install/conf/airtime-celery-cliff @@ -0,0 +1,25 @@ +# Names of nodes to start +CELERYD_NODES="airtime-celery-cliff" + +# Absolute or relative path to the 'celery' command: +CELERY_BIN="/usr/local/bin/celery" + +# App instance to use +CELERY_APP="airtime-celery.tasks:celery" + +# Extra command-line arguments to the worker +CELERYD_OPTS="--time-limit=300 --concurrency=1 --config=celeryconfig" + +# %N will be replaced with the first part of the nodename. +CELERYD_LOG_FILE="/var/log/airtime/%N.log" +CELERYD_PID_FILE="/var/run/celery/%N.pid" + +# Workers should run as an unprivileged user. +# You need to create this user manually (or you can choose +# a user/group combination that already exists, e.g. nobody). +CELERYD_USER="celery" +CELERYD_GROUP="celery" + +# If enabled pid and log directories will be created if missing, +# and owned by the userid/group configured. +CELERY_CREATE_DIRS=1 diff --git a/python_apps/airtime-celery/install/conf/airtime-celery-production b/python_apps/airtime-celery/install/conf/airtime-celery-production new file mode 100644 index 000000000..69f8e6b7a --- /dev/null +++ b/python_apps/airtime-celery/install/conf/airtime-celery-production @@ -0,0 +1,25 @@ +# Names of nodes to start +CELERYD_NODES="airtime-celery-production" + +# Absolute or relative path to the 'celery' command: +CELERY_BIN="/usr/local/bin/celery" + +# App instance to use +CELERY_APP="airtime-celery.tasks:celery" + +# Extra command-line arguments to the worker +CELERYD_OPTS="--time-limit=300 --concurrency=8 --config=celeryconfig" + +# %N will be replaced with the first part of the nodename. +CELERYD_LOG_FILE="/var/log/airtime/%N.log" +CELERYD_PID_FILE="/var/run/celery/%N.pid" + +# Workers should run as an unprivileged user. +# You need to create this user manually (or you can choose +# a user/group combination that already exists, e.g. nobody). +CELERYD_USER="celery" +CELERYD_GROUP="celery" + +# If enabled pid and log directories will be created if missing, +# and owned by the userid/group configured. +CELERY_CREATE_DIRS=1 diff --git a/python_apps/airtime-celery/install/conf/airtime-celery-staging b/python_apps/airtime-celery/install/conf/airtime-celery-staging new file mode 100644 index 000000000..e85252291 --- /dev/null +++ b/python_apps/airtime-celery/install/conf/airtime-celery-staging @@ -0,0 +1,25 @@ +# Names of nodes to start +CELERYD_NODES="airtime-celery-staging" + +# Absolute or relative path to the 'celery' command: +CELERY_BIN="/usr/local/bin/celery" + +# App instance to use +CELERY_APP="airtime-celery.tasks:celery" + +# Extra command-line arguments to the worker +CELERYD_OPTS="--time-limit=300 --concurrency=1 --config=celeryconfig" + +# %N will be replaced with the first part of the nodename. +CELERYD_LOG_FILE="/var/log/airtime/%N.log" +CELERYD_PID_FILE="/var/run/celery/%N.pid" + +# Workers should run as an unprivileged user. +# You need to create this user manually (or you can choose +# a user/group combination that already exists, e.g. nobody). +CELERYD_USER="celery" +CELERYD_GROUP="celery" + +# If enabled pid and log directories will be created if missing, +# and owned by the userid/group configured. +CELERY_CREATE_DIRS=1 diff --git a/python_apps/airtime-celery/install/upstart/airtime-celery b/python_apps/airtime-celery/install/initd/airtime-celery similarity index 99% rename from python_apps/airtime-celery/install/upstart/airtime-celery rename to python_apps/airtime-celery/install/initd/airtime-celery index 34e990ab8..d4f6ae89e 100644 --- a/python_apps/airtime-celery/install/upstart/airtime-celery +++ b/python_apps/airtime-celery/install/initd/airtime-celery @@ -37,6 +37,7 @@ if [ $(id -u) -ne 0 ]; then exit 1 fi +export RMQ_CONFIG_FILE="/etc/airtime/airtime.conf" # Can be a runlevel symlink (e.g. S02celeryd) if [ -L "$0" ]; then diff --git a/python_apps/airtime-celery/install/initd/airtime-celery-bananas b/python_apps/airtime-celery/install/initd/airtime-celery-bananas new file mode 100644 index 000000000..c91c36713 --- /dev/null +++ b/python_apps/airtime-celery/install/initd/airtime-celery-bananas @@ -0,0 +1,334 @@ +#!/bin/sh -e +# ============================================ +# celeryd - Starts the Celery worker daemon. +# ============================================ +# +# :Usage: /etc/init.d/celeryd {start|stop|force-reload|restart|try-restart|status} +# :Configuration file: /etc/default/celeryd +# +# See http://docs.celeryproject.org/en/latest/tutorials/daemonizing.html#generic-init-scripts + + +### BEGIN INIT INFO +# Provides: celeryd +# Required-Start: $network $local_fs $remote_fs +# Required-Stop: $network $local_fs $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: celery task worker daemon +### END INIT INFO +# +# +# To implement separate init scripts, copy this script and give it a different +# name: +# I.e., if my new application, "little-worker" needs an init, I +# should just use: +# +# cp /etc/init.d/celeryd /etc/init.d/little-worker +# +# You can then configure this by manipulating /etc/default/little-worker. +# +VERSION=10.1 +echo "celery init v${VERSION}." +if [ $(id -u) -ne 0 ]; then + echo "Error: This program can only be used by the root user." + echo " Unprivileged users must use the 'celery multi' utility, " + echo " or 'celery worker --detach'." + exit 1 +fi + +export RMQ_CONFIG_FILE="/etc/airtime-saas/bananas/rabbitmq-analyzer.ini" + +# Can be a runlevel symlink (e.g. S02celeryd) +if [ -L "$0" ]; then + SCRIPT_FILE=$(readlink "$0") +else + SCRIPT_FILE="$0" +fi +SCRIPT_NAME="$(basename "$SCRIPT_FILE")" + +DEFAULT_USER="celery" +DEFAULT_PID_FILE="/var/run/celery/%n.pid" +DEFAULT_LOG_FILE="/var/log/celery/%n.log" +DEFAULT_LOG_LEVEL="INFO" +DEFAULT_NODES="celery" +DEFAULT_CELERYD="-m celery worker --detach" + +CELERY_DEFAULTS=${CELERY_DEFAULTS:-"/etc/default/${SCRIPT_NAME}"} +# Make sure executable configuration script is owned by root +_config_sanity() { + local path="$1" + local owner=$(ls -ld "$path" | awk '{print $3}') + local iwgrp=$(ls -ld "$path" | cut -b 6) + local iwoth=$(ls -ld "$path" | cut -b 9) + if [ "$(id -u $owner)" != "0" ]; then + echo "Error: Config script '$path' must be owned by root!" + echo + echo "Resolution:" + echo "Review the file carefully and make sure it has not been " + echo "modified with mailicious intent. When sure the " + echo "script is safe to execute with superuser privileges " + echo "you can change ownership of the script:" + echo " $ sudo chown root '$path'" + exit 1 + fi + if [ "$iwoth" != "-" ]; then # S_IWOTH + echo "Error: Config script '$path' cannot be writable by others!" + echo + echo "Resolution:" + echo "Review the file carefully and make sure it has not been " + echo "modified with malicious intent. When sure the " + echo "script is safe to execute with superuser privileges " + echo "you can change the scripts permissions:" + echo " $ sudo chmod 640 '$path'" + exit 1 + fi + if [ "$iwgrp" != "-" ]; then # S_IWGRP + echo "Error: Config script '$path' cannot be writable by group!" + echo + echo "Resolution:" + echo "Review the file carefully and make sure it has not been " + echo "modified with malicious intent. When sure the " + echo "script is safe to execute with superuser privileges " + echo "you can change the scripts permissions:" + echo " $ sudo chmod 640 '$path'" + exit 1 + fi +} +if [ -f "$CELERY_DEFAULTS" ]; then + _config_sanity "$CELERY_DEFAULTS" + echo "Using config script: $CELERY_DEFAULTS" + . "$CELERY_DEFAULTS" +fi +# Sets --app argument for CELERY_BIN +CELERY_APP_ARG="" +if [ ! -z "$CELERY_APP" ]; then + CELERY_APP_ARG="--app=$CELERY_APP" +fi +CELERYD_USER=${CELERYD_USER:-$DEFAULT_USER} +# Set CELERY_CREATE_DIRS to always create log/pid dirs. +CELERY_CREATE_DIRS=${CELERY_CREATE_DIRS:-0} +CELERY_CREATE_RUNDIR=$CELERY_CREATE_DIRS +CELERY_CREATE_LOGDIR=$CELERY_CREATE_DIRS +if [ -z "$CELERYD_PID_FILE" ]; then + CELERYD_PID_FILE="$DEFAULT_PID_FILE" + CELERY_CREATE_RUNDIR=1 +fi +if [ -z "$CELERYD_LOG_FILE" ]; then + CELERYD_LOG_FILE="$DEFAULT_LOG_FILE" + CELERY_CREATE_LOGDIR=1 +fi +CELERYD_LOG_LEVEL=${CELERYD_LOG_LEVEL:-${CELERYD_LOGLEVEL:-$DEFAULT_LOG_LEVEL}} +CELERY_BIN=${CELERY_BIN:-"celery"} +CELERYD_MULTI=${CELERYD_MULTI:-"$CELERY_BIN multi"} +CELERYD_NODES=${CELERYD_NODES:-$DEFAULT_NODES} +export CELERY_LOADER +if [ -n "$2" ]; then + CELERYD_OPTS="$CELERYD_OPTS $2" +fi +CELERYD_LOG_DIR=`dirname $CELERYD_LOG_FILE` +CELERYD_PID_DIR=`dirname $CELERYD_PID_FILE` +# Extra start-stop-daemon options, like user/group. +if [ -n "$CELERYD_CHDIR" ]; then + DAEMON_OPTS="$DAEMON_OPTS --workdir=$CELERYD_CHDIR" +fi +check_dev_null() { + if [ ! -c /dev/null ]; then + echo "/dev/null is not a character device!" + exit 75 # EX_TEMPFAIL + fi +} +maybe_die() { + if [ $? -ne 0 ]; then + echo "Exiting: $* (errno $?)" + exit 77 # EX_NOPERM + fi +} +create_default_dir() { + if [ ! -d "$1" ]; then + echo "- Creating default directory: '$1'" + mkdir -p "$1" + maybe_die "Couldn't create directory $1" + echo "- Changing permissions of '$1' to 02755" + chmod 02755 "$1" + maybe_die "Couldn't change permissions for $1" + if [ -n "$CELERYD_USER" ]; then + echo "- Changing owner of '$1' to '$CELERYD_USER'" + chown "$CELERYD_USER" "$1" + maybe_die "Couldn't change owner of $1" + fi + if [ -n "$CELERYD_GROUP" ]; then + echo "- Changing group of '$1' to '$CELERYD_GROUP'" + chgrp "$CELERYD_GROUP" "$1" + maybe_die "Couldn't change group of $1" + fi + fi +} +check_paths() { + if [ $CELERY_CREATE_LOGDIR -eq 1 ]; then + create_default_dir "$CELERYD_LOG_DIR" + fi + if [ $CELERY_CREATE_RUNDIR -eq 1 ]; then + create_default_dir "$CELERYD_PID_DIR" + fi +} +create_paths() { + create_default_dir "$CELERYD_LOG_DIR" + create_default_dir "$CELERYD_PID_DIR" +} +export PATH="${PATH:+$PATH:}/usr/sbin:/sbin" +_get_pidfiles () { + # note: multi < 3.1.14 output to stderr, not stdout, hence the redirect. + ${CELERYD_MULTI} expand "${CELERYD_PID_FILE}" ${CELERYD_NODES} 2>&1 +} +_get_pids() { + found_pids=0 + my_exitcode=0 + for pidfile in $(_get_pidfiles); do + local pid=`cat "$pidfile"` + local cleaned_pid=`echo "$pid" | sed -e 's/[^0-9]//g'` + if [ -z "$pid" ] || [ "$cleaned_pid" != "$pid" ]; then + echo "bad pid file ($pidfile)" + one_failed=true + my_exitcode=1 + else + found_pids=1 + echo "$pid" + fi + if [ $found_pids -eq 0 ]; then + echo "${SCRIPT_NAME}: All nodes down" + exit $my_exitcode + fi + done +} +_chuid () { + su "$CELERYD_USER" -c "$CELERYD_MULTI $*" +} +start_workers () { + if [ ! -z "$CELERYD_ULIMIT" ]; then + ulimit $CELERYD_ULIMIT + fi + _chuid $* start $CELERYD_NODES $DAEMON_OPTS \ + --pidfile="$CELERYD_PID_FILE" \ + --logfile="$CELERYD_LOG_FILE" \ + --loglevel="$CELERYD_LOG_LEVEL" \ + $CELERY_APP_ARG \ + $CELERYD_OPTS +} +dryrun () { + (C_FAKEFORK=1 start_workers --verbose) +} +stop_workers () { + _chuid stopwait $CELERYD_NODES --pidfile="$CELERYD_PID_FILE" +} +restart_workers () { + _chuid restart $CELERYD_NODES $DAEMON_OPTS \ + --pidfile="$CELERYD_PID_FILE" \ + --logfile="$CELERYD_LOG_FILE" \ + --loglevel="$CELERYD_LOG_LEVEL" \ + $CELERY_APP_ARG \ + $CELERYD_OPTS +} +kill_workers() { + _chuid kill $CELERYD_NODES --pidfile="$CELERYD_PID_FILE" +} +restart_workers_graceful () { + echo "WARNING: Use with caution in production" + echo "The workers will attempt to restart, but they may not be able to." + local worker_pids= + worker_pids=`_get_pids` + [ "$one_failed" ] && exit 1 + for worker_pid in $worker_pids; do + local failed= + kill -HUP $worker_pid 2> /dev/null || failed=true + if [ "$failed" ]; then + echo "${SCRIPT_NAME} worker (pid $worker_pid) could not be restarted" + one_failed=true + else + echo "${SCRIPT_NAME} worker (pid $worker_pid) received SIGHUP" + fi + done + [ "$one_failed" ] && exit 1 || exit 0 +} +check_status () { + my_exitcode=0 + found_pids=0 + local one_failed= + for pidfile in $(_get_pidfiles); do + if [ ! -r $pidfile ]; then + echo "${SCRIPT_NAME} down: no pidfiles found" + one_failed=true + break + fi + local node=`basename "$pidfile" .pid` + local pid=`cat "$pidfile"` + local cleaned_pid=`echo "$pid" | sed -e 's/[^0-9]//g'` + if [ -z "$pid" ] || [ "$cleaned_pid" != "$pid" ]; then + echo "bad pid file ($pidfile)" + one_failed=true + else + local failed= + kill -0 $pid 2> /dev/null || failed=true + if [ "$failed" ]; then + echo "${SCRIPT_NAME} (node $node) (pid $pid) is down, but pidfile exists!" + one_failed=true + else + echo "${SCRIPT_NAME} (node $node) (pid $pid) is up..." + fi + fi + done + [ "$one_failed" ] && exit 1 || exit 0 +} +case "$1" in + start) + check_dev_null + check_paths + start_workers + ;; + stop) + check_dev_null + check_paths + stop_workers + ;; + reload|force-reload) + echo "Use restart" + ;; + status) + check_status + ;; + restart) + check_dev_null + check_paths + restart_workers + ;; + graceful) + check_dev_null + restart_workers_graceful + ;; + kill) + check_dev_null + kill_workers + ;; + dryrun) + check_dev_null + dryrun + ;; + try-restart) + check_dev_null + check_paths + restart_workers + ;; + create-paths) + check_dev_null + create_paths + ;; + check-paths) + check_dev_null + check_paths + ;; + *) + echo "Usage: /etc/init.d/${SCRIPT_NAME} {start|stop|restart|graceful|kill|dryrun|create-paths}" + exit 64 # EX_USAGE + ;; +esac +exit 0 \ No newline at end of file diff --git a/python_apps/airtime-celery/install/initd/airtime-celery-cliff b/python_apps/airtime-celery/install/initd/airtime-celery-cliff new file mode 100644 index 000000000..a2d0291a8 --- /dev/null +++ b/python_apps/airtime-celery/install/initd/airtime-celery-cliff @@ -0,0 +1,334 @@ +#!/bin/sh -e +# ============================================ +# celeryd - Starts the Celery worker daemon. +# ============================================ +# +# :Usage: /etc/init.d/celeryd {start|stop|force-reload|restart|try-restart|status} +# :Configuration file: /etc/default/celeryd +# +# See http://docs.celeryproject.org/en/latest/tutorials/daemonizing.html#generic-init-scripts + + +### BEGIN INIT INFO +# Provides: celeryd +# Required-Start: $network $local_fs $remote_fs +# Required-Stop: $network $local_fs $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: celery task worker daemon +### END INIT INFO +# +# +# To implement separate init scripts, copy this script and give it a different +# name: +# I.e., if my new application, "little-worker" needs an init, I +# should just use: +# +# cp /etc/init.d/celeryd /etc/init.d/little-worker +# +# You can then configure this by manipulating /etc/default/little-worker. +# +VERSION=10.1 +echo "celery init v${VERSION}." +if [ $(id -u) -ne 0 ]; then + echo "Error: This program can only be used by the root user." + echo " Unprivileged users must use the 'celery multi' utility, " + echo " or 'celery worker --detach'." + exit 1 +fi + +export RMQ_CONFIG_FILE="/etc/airtime-saas/cliff/rabbitmq-analyzer.ini" + +# Can be a runlevel symlink (e.g. S02celeryd) +if [ -L "$0" ]; then + SCRIPT_FILE=$(readlink "$0") +else + SCRIPT_FILE="$0" +fi +SCRIPT_NAME="$(basename "$SCRIPT_FILE")" + +DEFAULT_USER="celery" +DEFAULT_PID_FILE="/var/run/celery/%n.pid" +DEFAULT_LOG_FILE="/var/log/celery/%n.log" +DEFAULT_LOG_LEVEL="INFO" +DEFAULT_NODES="celery" +DEFAULT_CELERYD="-m celery worker --detach" + +CELERY_DEFAULTS=${CELERY_DEFAULTS:-"/etc/default/${SCRIPT_NAME}"} +# Make sure executable configuration script is owned by root +_config_sanity() { + local path="$1" + local owner=$(ls -ld "$path" | awk '{print $3}') + local iwgrp=$(ls -ld "$path" | cut -b 6) + local iwoth=$(ls -ld "$path" | cut -b 9) + if [ "$(id -u $owner)" != "0" ]; then + echo "Error: Config script '$path' must be owned by root!" + echo + echo "Resolution:" + echo "Review the file carefully and make sure it has not been " + echo "modified with mailicious intent. When sure the " + echo "script is safe to execute with superuser privileges " + echo "you can change ownership of the script:" + echo " $ sudo chown root '$path'" + exit 1 + fi + if [ "$iwoth" != "-" ]; then # S_IWOTH + echo "Error: Config script '$path' cannot be writable by others!" + echo + echo "Resolution:" + echo "Review the file carefully and make sure it has not been " + echo "modified with malicious intent. When sure the " + echo "script is safe to execute with superuser privileges " + echo "you can change the scripts permissions:" + echo " $ sudo chmod 640 '$path'" + exit 1 + fi + if [ "$iwgrp" != "-" ]; then # S_IWGRP + echo "Error: Config script '$path' cannot be writable by group!" + echo + echo "Resolution:" + echo "Review the file carefully and make sure it has not been " + echo "modified with malicious intent. When sure the " + echo "script is safe to execute with superuser privileges " + echo "you can change the scripts permissions:" + echo " $ sudo chmod 640 '$path'" + exit 1 + fi +} +if [ -f "$CELERY_DEFAULTS" ]; then + _config_sanity "$CELERY_DEFAULTS" + echo "Using config script: $CELERY_DEFAULTS" + . "$CELERY_DEFAULTS" +fi +# Sets --app argument for CELERY_BIN +CELERY_APP_ARG="" +if [ ! -z "$CELERY_APP" ]; then + CELERY_APP_ARG="--app=$CELERY_APP" +fi +CELERYD_USER=${CELERYD_USER:-$DEFAULT_USER} +# Set CELERY_CREATE_DIRS to always create log/pid dirs. +CELERY_CREATE_DIRS=${CELERY_CREATE_DIRS:-0} +CELERY_CREATE_RUNDIR=$CELERY_CREATE_DIRS +CELERY_CREATE_LOGDIR=$CELERY_CREATE_DIRS +if [ -z "$CELERYD_PID_FILE" ]; then + CELERYD_PID_FILE="$DEFAULT_PID_FILE" + CELERY_CREATE_RUNDIR=1 +fi +if [ -z "$CELERYD_LOG_FILE" ]; then + CELERYD_LOG_FILE="$DEFAULT_LOG_FILE" + CELERY_CREATE_LOGDIR=1 +fi +CELERYD_LOG_LEVEL=${CELERYD_LOG_LEVEL:-${CELERYD_LOGLEVEL:-$DEFAULT_LOG_LEVEL}} +CELERY_BIN=${CELERY_BIN:-"celery"} +CELERYD_MULTI=${CELERYD_MULTI:-"$CELERY_BIN multi"} +CELERYD_NODES=${CELERYD_NODES:-$DEFAULT_NODES} +export CELERY_LOADER +if [ -n "$2" ]; then + CELERYD_OPTS="$CELERYD_OPTS $2" +fi +CELERYD_LOG_DIR=`dirname $CELERYD_LOG_FILE` +CELERYD_PID_DIR=`dirname $CELERYD_PID_FILE` +# Extra start-stop-daemon options, like user/group. +if [ -n "$CELERYD_CHDIR" ]; then + DAEMON_OPTS="$DAEMON_OPTS --workdir=$CELERYD_CHDIR" +fi +check_dev_null() { + if [ ! -c /dev/null ]; then + echo "/dev/null is not a character device!" + exit 75 # EX_TEMPFAIL + fi +} +maybe_die() { + if [ $? -ne 0 ]; then + echo "Exiting: $* (errno $?)" + exit 77 # EX_NOPERM + fi +} +create_default_dir() { + if [ ! -d "$1" ]; then + echo "- Creating default directory: '$1'" + mkdir -p "$1" + maybe_die "Couldn't create directory $1" + echo "- Changing permissions of '$1' to 02755" + chmod 02755 "$1" + maybe_die "Couldn't change permissions for $1" + if [ -n "$CELERYD_USER" ]; then + echo "- Changing owner of '$1' to '$CELERYD_USER'" + chown "$CELERYD_USER" "$1" + maybe_die "Couldn't change owner of $1" + fi + if [ -n "$CELERYD_GROUP" ]; then + echo "- Changing group of '$1' to '$CELERYD_GROUP'" + chgrp "$CELERYD_GROUP" "$1" + maybe_die "Couldn't change group of $1" + fi + fi +} +check_paths() { + if [ $CELERY_CREATE_LOGDIR -eq 1 ]; then + create_default_dir "$CELERYD_LOG_DIR" + fi + if [ $CELERY_CREATE_RUNDIR -eq 1 ]; then + create_default_dir "$CELERYD_PID_DIR" + fi +} +create_paths() { + create_default_dir "$CELERYD_LOG_DIR" + create_default_dir "$CELERYD_PID_DIR" +} +export PATH="${PATH:+$PATH:}/usr/sbin:/sbin" +_get_pidfiles () { + # note: multi < 3.1.14 output to stderr, not stdout, hence the redirect. + ${CELERYD_MULTI} expand "${CELERYD_PID_FILE}" ${CELERYD_NODES} 2>&1 +} +_get_pids() { + found_pids=0 + my_exitcode=0 + for pidfile in $(_get_pidfiles); do + local pid=`cat "$pidfile"` + local cleaned_pid=`echo "$pid" | sed -e 's/[^0-9]//g'` + if [ -z "$pid" ] || [ "$cleaned_pid" != "$pid" ]; then + echo "bad pid file ($pidfile)" + one_failed=true + my_exitcode=1 + else + found_pids=1 + echo "$pid" + fi + if [ $found_pids -eq 0 ]; then + echo "${SCRIPT_NAME}: All nodes down" + exit $my_exitcode + fi + done +} +_chuid () { + su "$CELERYD_USER" -c "$CELERYD_MULTI $*" +} +start_workers () { + if [ ! -z "$CELERYD_ULIMIT" ]; then + ulimit $CELERYD_ULIMIT + fi + _chuid $* start $CELERYD_NODES $DAEMON_OPTS \ + --pidfile="$CELERYD_PID_FILE" \ + --logfile="$CELERYD_LOG_FILE" \ + --loglevel="$CELERYD_LOG_LEVEL" \ + $CELERY_APP_ARG \ + $CELERYD_OPTS +} +dryrun () { + (C_FAKEFORK=1 start_workers --verbose) +} +stop_workers () { + _chuid stopwait $CELERYD_NODES --pidfile="$CELERYD_PID_FILE" +} +restart_workers () { + _chuid restart $CELERYD_NODES $DAEMON_OPTS \ + --pidfile="$CELERYD_PID_FILE" \ + --logfile="$CELERYD_LOG_FILE" \ + --loglevel="$CELERYD_LOG_LEVEL" \ + $CELERY_APP_ARG \ + $CELERYD_OPTS +} +kill_workers() { + _chuid kill $CELERYD_NODES --pidfile="$CELERYD_PID_FILE" +} +restart_workers_graceful () { + echo "WARNING: Use with caution in production" + echo "The workers will attempt to restart, but they may not be able to." + local worker_pids= + worker_pids=`_get_pids` + [ "$one_failed" ] && exit 1 + for worker_pid in $worker_pids; do + local failed= + kill -HUP $worker_pid 2> /dev/null || failed=true + if [ "$failed" ]; then + echo "${SCRIPT_NAME} worker (pid $worker_pid) could not be restarted" + one_failed=true + else + echo "${SCRIPT_NAME} worker (pid $worker_pid) received SIGHUP" + fi + done + [ "$one_failed" ] && exit 1 || exit 0 +} +check_status () { + my_exitcode=0 + found_pids=0 + local one_failed= + for pidfile in $(_get_pidfiles); do + if [ ! -r $pidfile ]; then + echo "${SCRIPT_NAME} down: no pidfiles found" + one_failed=true + break + fi + local node=`basename "$pidfile" .pid` + local pid=`cat "$pidfile"` + local cleaned_pid=`echo "$pid" | sed -e 's/[^0-9]//g'` + if [ -z "$pid" ] || [ "$cleaned_pid" != "$pid" ]; then + echo "bad pid file ($pidfile)" + one_failed=true + else + local failed= + kill -0 $pid 2> /dev/null || failed=true + if [ "$failed" ]; then + echo "${SCRIPT_NAME} (node $node) (pid $pid) is down, but pidfile exists!" + one_failed=true + else + echo "${SCRIPT_NAME} (node $node) (pid $pid) is up..." + fi + fi + done + [ "$one_failed" ] && exit 1 || exit 0 +} +case "$1" in + start) + check_dev_null + check_paths + start_workers + ;; + stop) + check_dev_null + check_paths + stop_workers + ;; + reload|force-reload) + echo "Use restart" + ;; + status) + check_status + ;; + restart) + check_dev_null + check_paths + restart_workers + ;; + graceful) + check_dev_null + restart_workers_graceful + ;; + kill) + check_dev_null + kill_workers + ;; + dryrun) + check_dev_null + dryrun + ;; + try-restart) + check_dev_null + check_paths + restart_workers + ;; + create-paths) + check_dev_null + create_paths + ;; + check-paths) + check_dev_null + check_paths + ;; + *) + echo "Usage: /etc/init.d/${SCRIPT_NAME} {start|stop|restart|graceful|kill|dryrun|create-paths}" + exit 64 # EX_USAGE + ;; +esac +exit 0 \ No newline at end of file diff --git a/python_apps/airtime-celery/install/initd/airtime-celery-production b/python_apps/airtime-celery/install/initd/airtime-celery-production new file mode 100644 index 000000000..43f98d2d5 --- /dev/null +++ b/python_apps/airtime-celery/install/initd/airtime-celery-production @@ -0,0 +1,334 @@ +#!/bin/sh -e +# ============================================ +# celeryd - Starts the Celery worker daemon. +# ============================================ +# +# :Usage: /etc/init.d/celeryd {start|stop|force-reload|restart|try-restart|status} +# :Configuration file: /etc/default/celeryd +# +# See http://docs.celeryproject.org/en/latest/tutorials/daemonizing.html#generic-init-scripts + + +### BEGIN INIT INFO +# Provides: celeryd +# Required-Start: $network $local_fs $remote_fs +# Required-Stop: $network $local_fs $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: celery task worker daemon +### END INIT INFO +# +# +# To implement separate init scripts, copy this script and give it a different +# name: +# I.e., if my new application, "little-worker" needs an init, I +# should just use: +# +# cp /etc/init.d/celeryd /etc/init.d/little-worker +# +# You can then configure this by manipulating /etc/default/little-worker. +# +VERSION=10.1 +echo "celery init v${VERSION}." +if [ $(id -u) -ne 0 ]; then + echo "Error: This program can only be used by the root user." + echo " Unprivileged users must use the 'celery multi' utility, " + echo " or 'celery worker --detach'." + exit 1 +fi + +export RMQ_CONFIG_FILE="/etc/airtime-saas/production/rabbitmq-analyzer.ini" + +# Can be a runlevel symlink (e.g. S02celeryd) +if [ -L "$0" ]; then + SCRIPT_FILE=$(readlink "$0") +else + SCRIPT_FILE="$0" +fi +SCRIPT_NAME="$(basename "$SCRIPT_FILE")" + +DEFAULT_USER="celery" +DEFAULT_PID_FILE="/var/run/celery/%n.pid" +DEFAULT_LOG_FILE="/var/log/celery/%n.log" +DEFAULT_LOG_LEVEL="INFO" +DEFAULT_NODES="celery" +DEFAULT_CELERYD="-m celery worker --detach" + +CELERY_DEFAULTS=${CELERY_DEFAULTS:-"/etc/default/${SCRIPT_NAME}"} +# Make sure executable configuration script is owned by root +_config_sanity() { + local path="$1" + local owner=$(ls -ld "$path" | awk '{print $3}') + local iwgrp=$(ls -ld "$path" | cut -b 6) + local iwoth=$(ls -ld "$path" | cut -b 9) + if [ "$(id -u $owner)" != "0" ]; then + echo "Error: Config script '$path' must be owned by root!" + echo + echo "Resolution:" + echo "Review the file carefully and make sure it has not been " + echo "modified with mailicious intent. When sure the " + echo "script is safe to execute with superuser privileges " + echo "you can change ownership of the script:" + echo " $ sudo chown root '$path'" + exit 1 + fi + if [ "$iwoth" != "-" ]; then # S_IWOTH + echo "Error: Config script '$path' cannot be writable by others!" + echo + echo "Resolution:" + echo "Review the file carefully and make sure it has not been " + echo "modified with malicious intent. When sure the " + echo "script is safe to execute with superuser privileges " + echo "you can change the scripts permissions:" + echo " $ sudo chmod 640 '$path'" + exit 1 + fi + if [ "$iwgrp" != "-" ]; then # S_IWGRP + echo "Error: Config script '$path' cannot be writable by group!" + echo + echo "Resolution:" + echo "Review the file carefully and make sure it has not been " + echo "modified with malicious intent. When sure the " + echo "script is safe to execute with superuser privileges " + echo "you can change the scripts permissions:" + echo " $ sudo chmod 640 '$path'" + exit 1 + fi +} +if [ -f "$CELERY_DEFAULTS" ]; then + _config_sanity "$CELERY_DEFAULTS" + echo "Using config script: $CELERY_DEFAULTS" + . "$CELERY_DEFAULTS" +fi +# Sets --app argument for CELERY_BIN +CELERY_APP_ARG="" +if [ ! -z "$CELERY_APP" ]; then + CELERY_APP_ARG="--app=$CELERY_APP" +fi +CELERYD_USER=${CELERYD_USER:-$DEFAULT_USER} +# Set CELERY_CREATE_DIRS to always create log/pid dirs. +CELERY_CREATE_DIRS=${CELERY_CREATE_DIRS:-0} +CELERY_CREATE_RUNDIR=$CELERY_CREATE_DIRS +CELERY_CREATE_LOGDIR=$CELERY_CREATE_DIRS +if [ -z "$CELERYD_PID_FILE" ]; then + CELERYD_PID_FILE="$DEFAULT_PID_FILE" + CELERY_CREATE_RUNDIR=1 +fi +if [ -z "$CELERYD_LOG_FILE" ]; then + CELERYD_LOG_FILE="$DEFAULT_LOG_FILE" + CELERY_CREATE_LOGDIR=1 +fi +CELERYD_LOG_LEVEL=${CELERYD_LOG_LEVEL:-${CELERYD_LOGLEVEL:-$DEFAULT_LOG_LEVEL}} +CELERY_BIN=${CELERY_BIN:-"celery"} +CELERYD_MULTI=${CELERYD_MULTI:-"$CELERY_BIN multi"} +CELERYD_NODES=${CELERYD_NODES:-$DEFAULT_NODES} +export CELERY_LOADER +if [ -n "$2" ]; then + CELERYD_OPTS="$CELERYD_OPTS $2" +fi +CELERYD_LOG_DIR=`dirname $CELERYD_LOG_FILE` +CELERYD_PID_DIR=`dirname $CELERYD_PID_FILE` +# Extra start-stop-daemon options, like user/group. +if [ -n "$CELERYD_CHDIR" ]; then + DAEMON_OPTS="$DAEMON_OPTS --workdir=$CELERYD_CHDIR" +fi +check_dev_null() { + if [ ! -c /dev/null ]; then + echo "/dev/null is not a character device!" + exit 75 # EX_TEMPFAIL + fi +} +maybe_die() { + if [ $? -ne 0 ]; then + echo "Exiting: $* (errno $?)" + exit 77 # EX_NOPERM + fi +} +create_default_dir() { + if [ ! -d "$1" ]; then + echo "- Creating default directory: '$1'" + mkdir -p "$1" + maybe_die "Couldn't create directory $1" + echo "- Changing permissions of '$1' to 02755" + chmod 02755 "$1" + maybe_die "Couldn't change permissions for $1" + if [ -n "$CELERYD_USER" ]; then + echo "- Changing owner of '$1' to '$CELERYD_USER'" + chown "$CELERYD_USER" "$1" + maybe_die "Couldn't change owner of $1" + fi + if [ -n "$CELERYD_GROUP" ]; then + echo "- Changing group of '$1' to '$CELERYD_GROUP'" + chgrp "$CELERYD_GROUP" "$1" + maybe_die "Couldn't change group of $1" + fi + fi +} +check_paths() { + if [ $CELERY_CREATE_LOGDIR -eq 1 ]; then + create_default_dir "$CELERYD_LOG_DIR" + fi + if [ $CELERY_CREATE_RUNDIR -eq 1 ]; then + create_default_dir "$CELERYD_PID_DIR" + fi +} +create_paths() { + create_default_dir "$CELERYD_LOG_DIR" + create_default_dir "$CELERYD_PID_DIR" +} +export PATH="${PATH:+$PATH:}/usr/sbin:/sbin" +_get_pidfiles () { + # note: multi < 3.1.14 output to stderr, not stdout, hence the redirect. + ${CELERYD_MULTI} expand "${CELERYD_PID_FILE}" ${CELERYD_NODES} 2>&1 +} +_get_pids() { + found_pids=0 + my_exitcode=0 + for pidfile in $(_get_pidfiles); do + local pid=`cat "$pidfile"` + local cleaned_pid=`echo "$pid" | sed -e 's/[^0-9]//g'` + if [ -z "$pid" ] || [ "$cleaned_pid" != "$pid" ]; then + echo "bad pid file ($pidfile)" + one_failed=true + my_exitcode=1 + else + found_pids=1 + echo "$pid" + fi + if [ $found_pids -eq 0 ]; then + echo "${SCRIPT_NAME}: All nodes down" + exit $my_exitcode + fi + done +} +_chuid () { + su "$CELERYD_USER" -c "$CELERYD_MULTI $*" +} +start_workers () { + if [ ! -z "$CELERYD_ULIMIT" ]; then + ulimit $CELERYD_ULIMIT + fi + _chuid $* start $CELERYD_NODES $DAEMON_OPTS \ + --pidfile="$CELERYD_PID_FILE" \ + --logfile="$CELERYD_LOG_FILE" \ + --loglevel="$CELERYD_LOG_LEVEL" \ + $CELERY_APP_ARG \ + $CELERYD_OPTS +} +dryrun () { + (C_FAKEFORK=1 start_workers --verbose) +} +stop_workers () { + _chuid stopwait $CELERYD_NODES --pidfile="$CELERYD_PID_FILE" +} +restart_workers () { + _chuid restart $CELERYD_NODES $DAEMON_OPTS \ + --pidfile="$CELERYD_PID_FILE" \ + --logfile="$CELERYD_LOG_FILE" \ + --loglevel="$CELERYD_LOG_LEVEL" \ + $CELERY_APP_ARG \ + $CELERYD_OPTS +} +kill_workers() { + _chuid kill $CELERYD_NODES --pidfile="$CELERYD_PID_FILE" +} +restart_workers_graceful () { + echo "WARNING: Use with caution in production" + echo "The workers will attempt to restart, but they may not be able to." + local worker_pids= + worker_pids=`_get_pids` + [ "$one_failed" ] && exit 1 + for worker_pid in $worker_pids; do + local failed= + kill -HUP $worker_pid 2> /dev/null || failed=true + if [ "$failed" ]; then + echo "${SCRIPT_NAME} worker (pid $worker_pid) could not be restarted" + one_failed=true + else + echo "${SCRIPT_NAME} worker (pid $worker_pid) received SIGHUP" + fi + done + [ "$one_failed" ] && exit 1 || exit 0 +} +check_status () { + my_exitcode=0 + found_pids=0 + local one_failed= + for pidfile in $(_get_pidfiles); do + if [ ! -r $pidfile ]; then + echo "${SCRIPT_NAME} down: no pidfiles found" + one_failed=true + break + fi + local node=`basename "$pidfile" .pid` + local pid=`cat "$pidfile"` + local cleaned_pid=`echo "$pid" | sed -e 's/[^0-9]//g'` + if [ -z "$pid" ] || [ "$cleaned_pid" != "$pid" ]; then + echo "bad pid file ($pidfile)" + one_failed=true + else + local failed= + kill -0 $pid 2> /dev/null || failed=true + if [ "$failed" ]; then + echo "${SCRIPT_NAME} (node $node) (pid $pid) is down, but pidfile exists!" + one_failed=true + else + echo "${SCRIPT_NAME} (node $node) (pid $pid) is up..." + fi + fi + done + [ "$one_failed" ] && exit 1 || exit 0 +} +case "$1" in + start) + check_dev_null + check_paths + start_workers + ;; + stop) + check_dev_null + check_paths + stop_workers + ;; + reload|force-reload) + echo "Use restart" + ;; + status) + check_status + ;; + restart) + check_dev_null + check_paths + restart_workers + ;; + graceful) + check_dev_null + restart_workers_graceful + ;; + kill) + check_dev_null + kill_workers + ;; + dryrun) + check_dev_null + dryrun + ;; + try-restart) + check_dev_null + check_paths + restart_workers + ;; + create-paths) + check_dev_null + create_paths + ;; + check-paths) + check_dev_null + check_paths + ;; + *) + echo "Usage: /etc/init.d/${SCRIPT_NAME} {start|stop|restart|graceful|kill|dryrun|create-paths}" + exit 64 # EX_USAGE + ;; +esac +exit 0 \ No newline at end of file diff --git a/python_apps/airtime-celery/install/initd/airtime-celery-staging b/python_apps/airtime-celery/install/initd/airtime-celery-staging new file mode 100644 index 000000000..bed848a77 --- /dev/null +++ b/python_apps/airtime-celery/install/initd/airtime-celery-staging @@ -0,0 +1,334 @@ +#!/bin/sh -e +# ============================================ +# celeryd - Starts the Celery worker daemon. +# ============================================ +# +# :Usage: /etc/init.d/celeryd {start|stop|force-reload|restart|try-restart|status} +# :Configuration file: /etc/default/celeryd +# +# See http://docs.celeryproject.org/en/latest/tutorials/daemonizing.html#generic-init-scripts + + +### BEGIN INIT INFO +# Provides: celeryd +# Required-Start: $network $local_fs $remote_fs +# Required-Stop: $network $local_fs $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: celery task worker daemon +### END INIT INFO +# +# +# To implement separate init scripts, copy this script and give it a different +# name: +# I.e., if my new application, "little-worker" needs an init, I +# should just use: +# +# cp /etc/init.d/celeryd /etc/init.d/little-worker +# +# You can then configure this by manipulating /etc/default/little-worker. +# +VERSION=10.1 +echo "celery init v${VERSION}." +if [ $(id -u) -ne 0 ]; then + echo "Error: This program can only be used by the root user." + echo " Unprivileged users must use the 'celery multi' utility, " + echo " or 'celery worker --detach'." + exit 1 +fi + +export RMQ_CONFIG_FILE="/etc/airtime-saas/staging/rabbitmq-analyzer.ini" + +# Can be a runlevel symlink (e.g. S02celeryd) +if [ -L "$0" ]; then + SCRIPT_FILE=$(readlink "$0") +else + SCRIPT_FILE="$0" +fi +SCRIPT_NAME="$(basename "$SCRIPT_FILE")" + +DEFAULT_USER="celery" +DEFAULT_PID_FILE="/var/run/celery/%n.pid" +DEFAULT_LOG_FILE="/var/log/celery/%n.log" +DEFAULT_LOG_LEVEL="INFO" +DEFAULT_NODES="celery" +DEFAULT_CELERYD="-m celery worker --detach" + +CELERY_DEFAULTS=${CELERY_DEFAULTS:-"/etc/default/${SCRIPT_NAME}"} +# Make sure executable configuration script is owned by root +_config_sanity() { + local path="$1" + local owner=$(ls -ld "$path" | awk '{print $3}') + local iwgrp=$(ls -ld "$path" | cut -b 6) + local iwoth=$(ls -ld "$path" | cut -b 9) + if [ "$(id -u $owner)" != "0" ]; then + echo "Error: Config script '$path' must be owned by root!" + echo + echo "Resolution:" + echo "Review the file carefully and make sure it has not been " + echo "modified with mailicious intent. When sure the " + echo "script is safe to execute with superuser privileges " + echo "you can change ownership of the script:" + echo " $ sudo chown root '$path'" + exit 1 + fi + if [ "$iwoth" != "-" ]; then # S_IWOTH + echo "Error: Config script '$path' cannot be writable by others!" + echo + echo "Resolution:" + echo "Review the file carefully and make sure it has not been " + echo "modified with malicious intent. When sure the " + echo "script is safe to execute with superuser privileges " + echo "you can change the scripts permissions:" + echo " $ sudo chmod 640 '$path'" + exit 1 + fi + if [ "$iwgrp" != "-" ]; then # S_IWGRP + echo "Error: Config script '$path' cannot be writable by group!" + echo + echo "Resolution:" + echo "Review the file carefully and make sure it has not been " + echo "modified with malicious intent. When sure the " + echo "script is safe to execute with superuser privileges " + echo "you can change the scripts permissions:" + echo " $ sudo chmod 640 '$path'" + exit 1 + fi +} +if [ -f "$CELERY_DEFAULTS" ]; then + _config_sanity "$CELERY_DEFAULTS" + echo "Using config script: $CELERY_DEFAULTS" + . "$CELERY_DEFAULTS" +fi +# Sets --app argument for CELERY_BIN +CELERY_APP_ARG="" +if [ ! -z "$CELERY_APP" ]; then + CELERY_APP_ARG="--app=$CELERY_APP" +fi +CELERYD_USER=${CELERYD_USER:-$DEFAULT_USER} +# Set CELERY_CREATE_DIRS to always create log/pid dirs. +CELERY_CREATE_DIRS=${CELERY_CREATE_DIRS:-0} +CELERY_CREATE_RUNDIR=$CELERY_CREATE_DIRS +CELERY_CREATE_LOGDIR=$CELERY_CREATE_DIRS +if [ -z "$CELERYD_PID_FILE" ]; then + CELERYD_PID_FILE="$DEFAULT_PID_FILE" + CELERY_CREATE_RUNDIR=1 +fi +if [ -z "$CELERYD_LOG_FILE" ]; then + CELERYD_LOG_FILE="$DEFAULT_LOG_FILE" + CELERY_CREATE_LOGDIR=1 +fi +CELERYD_LOG_LEVEL=${CELERYD_LOG_LEVEL:-${CELERYD_LOGLEVEL:-$DEFAULT_LOG_LEVEL}} +CELERY_BIN=${CELERY_BIN:-"celery"} +CELERYD_MULTI=${CELERYD_MULTI:-"$CELERY_BIN multi"} +CELERYD_NODES=${CELERYD_NODES:-$DEFAULT_NODES} +export CELERY_LOADER +if [ -n "$2" ]; then + CELERYD_OPTS="$CELERYD_OPTS $2" +fi +CELERYD_LOG_DIR=`dirname $CELERYD_LOG_FILE` +CELERYD_PID_DIR=`dirname $CELERYD_PID_FILE` +# Extra start-stop-daemon options, like user/group. +if [ -n "$CELERYD_CHDIR" ]; then + DAEMON_OPTS="$DAEMON_OPTS --workdir=$CELERYD_CHDIR" +fi +check_dev_null() { + if [ ! -c /dev/null ]; then + echo "/dev/null is not a character device!" + exit 75 # EX_TEMPFAIL + fi +} +maybe_die() { + if [ $? -ne 0 ]; then + echo "Exiting: $* (errno $?)" + exit 77 # EX_NOPERM + fi +} +create_default_dir() { + if [ ! -d "$1" ]; then + echo "- Creating default directory: '$1'" + mkdir -p "$1" + maybe_die "Couldn't create directory $1" + echo "- Changing permissions of '$1' to 02755" + chmod 02755 "$1" + maybe_die "Couldn't change permissions for $1" + if [ -n "$CELERYD_USER" ]; then + echo "- Changing owner of '$1' to '$CELERYD_USER'" + chown "$CELERYD_USER" "$1" + maybe_die "Couldn't change owner of $1" + fi + if [ -n "$CELERYD_GROUP" ]; then + echo "- Changing group of '$1' to '$CELERYD_GROUP'" + chgrp "$CELERYD_GROUP" "$1" + maybe_die "Couldn't change group of $1" + fi + fi +} +check_paths() { + if [ $CELERY_CREATE_LOGDIR -eq 1 ]; then + create_default_dir "$CELERYD_LOG_DIR" + fi + if [ $CELERY_CREATE_RUNDIR -eq 1 ]; then + create_default_dir "$CELERYD_PID_DIR" + fi +} +create_paths() { + create_default_dir "$CELERYD_LOG_DIR" + create_default_dir "$CELERYD_PID_DIR" +} +export PATH="${PATH:+$PATH:}/usr/sbin:/sbin" +_get_pidfiles () { + # note: multi < 3.1.14 output to stderr, not stdout, hence the redirect. + ${CELERYD_MULTI} expand "${CELERYD_PID_FILE}" ${CELERYD_NODES} 2>&1 +} +_get_pids() { + found_pids=0 + my_exitcode=0 + for pidfile in $(_get_pidfiles); do + local pid=`cat "$pidfile"` + local cleaned_pid=`echo "$pid" | sed -e 's/[^0-9]//g'` + if [ -z "$pid" ] || [ "$cleaned_pid" != "$pid" ]; then + echo "bad pid file ($pidfile)" + one_failed=true + my_exitcode=1 + else + found_pids=1 + echo "$pid" + fi + if [ $found_pids -eq 0 ]; then + echo "${SCRIPT_NAME}: All nodes down" + exit $my_exitcode + fi + done +} +_chuid () { + su "$CELERYD_USER" -c "$CELERYD_MULTI $*" +} +start_workers () { + if [ ! -z "$CELERYD_ULIMIT" ]; then + ulimit $CELERYD_ULIMIT + fi + _chuid $* start $CELERYD_NODES $DAEMON_OPTS \ + --pidfile="$CELERYD_PID_FILE" \ + --logfile="$CELERYD_LOG_FILE" \ + --loglevel="$CELERYD_LOG_LEVEL" \ + $CELERY_APP_ARG \ + $CELERYD_OPTS +} +dryrun () { + (C_FAKEFORK=1 start_workers --verbose) +} +stop_workers () { + _chuid stopwait $CELERYD_NODES --pidfile="$CELERYD_PID_FILE" +} +restart_workers () { + _chuid restart $CELERYD_NODES $DAEMON_OPTS \ + --pidfile="$CELERYD_PID_FILE" \ + --logfile="$CELERYD_LOG_FILE" \ + --loglevel="$CELERYD_LOG_LEVEL" \ + $CELERY_APP_ARG \ + $CELERYD_OPTS +} +kill_workers() { + _chuid kill $CELERYD_NODES --pidfile="$CELERYD_PID_FILE" +} +restart_workers_graceful () { + echo "WARNING: Use with caution in production" + echo "The workers will attempt to restart, but they may not be able to." + local worker_pids= + worker_pids=`_get_pids` + [ "$one_failed" ] && exit 1 + for worker_pid in $worker_pids; do + local failed= + kill -HUP $worker_pid 2> /dev/null || failed=true + if [ "$failed" ]; then + echo "${SCRIPT_NAME} worker (pid $worker_pid) could not be restarted" + one_failed=true + else + echo "${SCRIPT_NAME} worker (pid $worker_pid) received SIGHUP" + fi + done + [ "$one_failed" ] && exit 1 || exit 0 +} +check_status () { + my_exitcode=0 + found_pids=0 + local one_failed= + for pidfile in $(_get_pidfiles); do + if [ ! -r $pidfile ]; then + echo "${SCRIPT_NAME} down: no pidfiles found" + one_failed=true + break + fi + local node=`basename "$pidfile" .pid` + local pid=`cat "$pidfile"` + local cleaned_pid=`echo "$pid" | sed -e 's/[^0-9]//g'` + if [ -z "$pid" ] || [ "$cleaned_pid" != "$pid" ]; then + echo "bad pid file ($pidfile)" + one_failed=true + else + local failed= + kill -0 $pid 2> /dev/null || failed=true + if [ "$failed" ]; then + echo "${SCRIPT_NAME} (node $node) (pid $pid) is down, but pidfile exists!" + one_failed=true + else + echo "${SCRIPT_NAME} (node $node) (pid $pid) is up..." + fi + fi + done + [ "$one_failed" ] && exit 1 || exit 0 +} +case "$1" in + start) + check_dev_null + check_paths + start_workers + ;; + stop) + check_dev_null + check_paths + stop_workers + ;; + reload|force-reload) + echo "Use restart" + ;; + status) + check_status + ;; + restart) + check_dev_null + check_paths + restart_workers + ;; + graceful) + check_dev_null + restart_workers_graceful + ;; + kill) + check_dev_null + kill_workers + ;; + dryrun) + check_dev_null + dryrun + ;; + try-restart) + check_dev_null + check_paths + restart_workers + ;; + create-paths) + check_dev_null + create_paths + ;; + check-paths) + check_dev_null + check_paths + ;; + *) + echo "Usage: /etc/init.d/${SCRIPT_NAME} {start|stop|restart|graceful|kill|dryrun|create-paths}" + exit 64 # EX_USAGE + ;; +esac +exit 0 \ No newline at end of file diff --git a/python_apps/airtime-celery/setup.py b/python_apps/airtime-celery/setup.py index 6f40351b7..96a5ef736 100644 --- a/python_apps/airtime-celery/setup.py +++ b/python_apps/airtime-celery/setup.py @@ -2,15 +2,26 @@ from setuptools import setup from subprocess import call import os import sys +from glob import glob install_args = ['install', 'install_data', 'develop'] -# Definitely not the best way of doing this... +# XXX Definitely not the best way of doing this... quite possibly the literal worst! if sys.argv[1] in install_args: data_files = [('/etc/default', ['install/conf/airtime-celery']), - ('/etc/init.d', ['install/upstart/airtime-celery'])] + ('/etc/init.d', ['install/initd/airtime-celery'])] + for i, arg in enumerate(sys.argv): + if "--dev-env" in arg: + env = arg.split('=')[1] + data_files = [('/etc/default', ['install/conf/airtime-celery-%s' % env]), + ('/etc/init.d', ['install/initd/airtime-celery-%s' % env])] + sys.argv.remove(arg) + elif arg == "--all-envs": + data_files = ([('/etc/default', glob('install/conf/*')), + ('/etc/init.d', glob('install/initd/*'))]) + sys.argv.remove(arg) else: - data_files = [] + scripts = data_files = [] def postinst(): @@ -18,11 +29,13 @@ def postinst(): call(['initctl', 'reload-configuration']) # Make /etc/init.d file executable and set proper # permissions for the defaults config file - os.chmod('/etc/init.d/airtime-celery', 0755) - os.chmod('/etc/default/airtime-celery', 0640) - print "Setting uploader to start on boot" - call(['update-rc.d', 'airtime-celery', 'defaults']) - print "Run \"sudo service airtime-celery restart\" now." + for f in glob('/etc/init.d/airtime-celery*'): + os.chmod(f, 0755) + for f in glob('/etc/default/airtime-celery*'): + os.chmod(f, 0640) + # print "Setting Celery to start on boot" + # call(['update-rc.d', 'airtime-celery', 'defaults']) + print "Run \"sudo service airtime-celery restart\" or \"sudo service airtime-celery-%DEV_ENV% restart\" now." setup(name='airtime-celery', version='0.1', @@ -42,4 +55,3 @@ setup(name='airtime-celery', if data_files: postinst() -