Пример #1
0
 public function defaultAction()
 {
     if (!$this->user->isAdmin()) {
         throw new Scalr_Exception_InsufficientPermissions();
     }
     $this->response->page('ui/analytics/projects/view.js', array('projects' => $this->getProjectsList(), 'quarters' => SettingEntity::getQuarters(true)), array('/ui/analytics/analytics.js'), array('/ui/analytics/analytics.css'));
 }
Пример #2
0
 public function getContent($params = array())
 {
     $this->request->restrictAccess(Acl::RESOURCE_OWN_FARMS, Acl::PERM_FARMS_CREATE);
     $projects = [];
     if ($this->getContainer()->analytics->enabled && $this->getEnvironment()->getPlatformConfigValue(Scalr_Environment::SETTING_CC_ID)) {
         $costCenter = $this->getContainer()->analytics->ccs->get($this->getEnvironment()->getPlatformConfigValue(Scalr_Environment::SETTING_CC_ID));
         $currentYear = (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y');
         $quarters = new Quarters(SettingEntity::getQuarters());
         $currentQuarter = $quarters->getQuarterForDate(new \DateTime('now', new \DateTimeZone('UTC')));
         if ($costCenter instanceof CostCentreEntity) {
             $projectsIterator = new SharedProjectsFilterIterator($costCenter->getProjects(), $costCenter->ccId, $this->user, $this->getEnvironment());
             foreach ($projectsIterator as $item) {
                 $quarterBudget = QuarterlyBudgetEntity::findOne([['year' => $currentYear], ['subjectType' => QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT], ['subjectId' => $item->projectId], ['quarter' => $currentQuarter]]);
                 $projects[] = array('projectId' => $item->projectId, 'name' => $item->name, 'budgetRemain' => !is_null($quarterBudget) && $quarterBudget->budget > 0 ? max(0, round($quarterBudget->budget - $quarterBudget->cumulativespend)) : null);
             }
             //$costCentreName = $costCenter->name;
             $isLocked = $costCenter->getProperty(CostCentrePropertyEntity::NAME_LOCKED);
             $accountCcs = AccountCostCenterEntity::findOne([['accountId' => $this->environment->clientId], ['ccId' => $costCenter->ccId]]);
             if ($isLocked || !$accountCcs instanceof AccountCostCenterEntity) {
                 $costCentreLocked = 1;
             } else {
                 $costCentreLocked = 0;
             }
         } else {
             $costCentreName = '';
             $costCentreLocked = 0;
         }
     }
     return ['costCenterLocked' => $costCentreLocked, 'projects' => $projects];
 }
Пример #3
0
 /**
  * {@inheritdoc}
  * @see \Scalr\System\Zmq\Cron\TaskInterface::enqueue()
  */
 public function enqueue()
 {
     $queue = new ArrayObject([]);
     if (!\Scalr::getContainer()->analytics->enabled) {
         $this->log("INFO", "Terminating the process as Cost analytics is disabled in the config.");
         exit;
     }
     if (SettingEntity::getValue(SettingEntity::ID_FORBID_AUTOMATIC_UPDATE_AWS_PRICES)) {
         $this->log("INFO", "Terminating the process because of overriding AWS prices has been forbidden by financial admin.");
         exit;
     }
     $now = new DateTime('now', new DateTimeZone('UTC'));
     $urls = array('https://a0.awsstatic.com/pricing/1/ec2/linux-od.min.js', 'https://a0.awsstatic.com/pricing/1/ec2/mswin-od.min.js', 'https://a0.awsstatic.com/pricing/1/ec2/previous-generation/linux-od.min.js', 'https://a0.awsstatic.com/pricing/1/ec2/previous-generation/mswin-od.min.js');
     $availableLocations = Aws::getCloudLocations();
     foreach ($urls as $link) {
         $json = trim(preg_replace('/^.+?callback\\((.+?)\\);\\s*$/sU', '\\1', $this->getPricingContent($link)));
         $data = json_decode(preg_replace('/(\\w+):/', '"\\1":', $json));
         if (!empty($data->config->regions)) {
             foreach ($data->config->regions as $rd) {
                 $rd->url = basename($link);
                 $queue->append($rd);
             }
         }
     }
     return $queue;
 }
Пример #4
0
 protected function run1($stage)
 {
     $this->console->out('Initializing quarterly_budget data...');
     $quarters = new Quarters(SettingEntity::getQuarters());
     $currentYearPeriod = $quarters->getPeriodForDate();
     $currentFiscalYear = $currentYearPeriod->year;
     $prevFiscalYear = $currentFiscalYear - 1;
     $this->db->Execute("\n            UPDATE quarterly_budget b\n            SET b.`cumulativespend` = 0.000000\n            WHERE b.`year` = ? OR b.`year` = ?\n        ", [$currentFiscalYear, $prevFiscalYear]);
     foreach ([$prevFiscalYear, $currentFiscalYear] as $year) {
         for ($quarter = 1; $quarter <= 4; $quarter++) {
             $quarterPeriod = $quarters->getPeriodForQuarter($quarter, $year);
             $this->db->Execute("\n                    INSERT INTO quarterly_budget (`year`, `quarter`, `subject_type`, `subject_id`, `cumulativespend`)\n                    SELECT ?, u.`quarter`, u.`subject_type`, u.`subject_id`, u.`cumulativespend`\n                    FROM (\n                        SELECT ? AS `quarter`, ? AS `subject_type`, `cc_id` AS `subject_id`, SUM(`cost`) AS `cumulativespend`\n                        FROM usage_d\n                        WHERE `date` BETWEEN ? AND ?\n                        GROUP BY `cc_id`\n                        UNION ALL\n                        SELECT ? AS `quarter`, ? AS `subject_type`, `project_id` AS `subject_id`, SUM(`cost`) AS `cumulativespend`\n                        FROM usage_d\n                        WHERE `date` BETWEEN ? AND ?\n                        GROUP BY `project_id`\n                    ) AS u\n                    ON DUPLICATE KEY UPDATE\n                        `cumulativespend` = u.`cumulativespend`\n                ", [$year, $quarter, QuarterlyBudgetEntity::SUBJECT_TYPE_CC, $quarterPeriod->start->format('Y-m-d'), $quarterPeriod->end->format('Y-m-d'), $quarter, QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT, $quarterPeriod->start->format('Y-m-d'), $quarterPeriod->end->format('Y-m-d')]);
             $this->db->Execute("\n                    UPDATE quarterly_budget b\n                    JOIN (\n                        (SELECT SUM(`cost`) AS `cost`, `date`, `cc_id` AS `subject_id`\n                        FROM usage_d\n                        WHERE `date` BETWEEN ? AND ?\n                        GROUP BY `cc_id`, `date`)\n                        UNION ALL\n                        (SELECT SUM(`cost`) AS `cost`, `date`, `project_id` AS `subject_id`\n                        FROM usage_d\n                        WHERE `date` BETWEEN ? AND ?\n                        GROUP BY `project_id`, `date`)\n                        ORDER BY `date`\n                    ) AS ud ON b.subject_id = ud.`subject_id` AND b.`year` = ?\n                    SET b.`spentondate` = IF (\n                        b.`budget` > 0 AND b.`spentondate` IS NULL AND ud.`cost` >= b.`budget`,\n                        ud.`date`,\n                        b.`spentondate`\n                    )\n                    WHERE b.`quarter` = ?\n                ", [$quarterPeriod->start->format('Y-m-d'), $quarterPeriod->end->format('Y-m-d'), $quarterPeriod->start->format('Y-m-d'), $quarterPeriod->end->format('Y-m-d'), $year, $quarter]);
         }
     }
 }
Пример #5
0
 private function saveReports($id, $settings)
 {
     SettingEntity::setValue($id, $settings['enabled']);
     $uuids = array();
     foreach ($settings['items'] as $item) {
         $report = new ReportEntity();
         if ($item['uuid']) {
             $report->findPk($item['uuid']);
         }
         $report->subjectType = $item['subjectType'];
         $subject = null;
         if ($report->subjectType == ReportEntity::SUBJECT_TYPE_CC) {
             $subject = $this->getContainer()->analytics->ccs->get($item['subjectId']);
         } elseif ($report->subjectType == ReportEntity::SUBJECT_TYPE_PROJECT) {
             $subject = $this->getContainer()->analytics->projects->get($item['subjectId']);
         } else {
             $report->subjectType = null;
             $report->subjectId = null;
         }
         if ($report->subjectType) {
             if ($item['subjectId'] && !$subject) {
                 throw new Scalr_UI_Exception_NotFound();
             }
             $report->subjectId = $item['subjectId'] ? $item['subjectId'] : null;
         }
         $report->period = $item['period'];
         $report->emails = $item['emails'];
         $report->save();
         $uuids[] = $report->uuid;
     }
     foreach (ReportEntity::all() as $report) {
         if (!in_array($report->uuid, $uuids)) {
             $report->delete();
         }
     }
 }
Пример #6
0
 /**
  * Default action
  */
 public function defaultAction()
 {
     $this->response->page('ui/account2/analytics/projects/view.js', ['quarters' => SettingEntity::getQuarters(true), 'projects' => $this->getProjectsList()], ['ui/analytics/analytics.js'], ['ui/analytics/analytics.css', 'ui/admin/analytics/admin.css']);
 }
Пример #7
0
 /**
  * {@inheritdoc}
  * @see \Scalr\System\Pcntl\ProcessInterface::OnStartForking()
  */
 public function OnStartForking()
 {
     if (!\Scalr::getContainer()->analytics->enabled) {
         die("Terminating the process as Cost analytics is disabled in the config.\n");
     }
     if (SettingEntity::getValue(SettingEntity::ID_FORBID_AUTOMATIC_UPDATE_AWS_PRICES)) {
         die("Terminating the process because of overriding AWS prices has been forbidden by financial admin.\n");
     }
     $now = new DateTime('now', new DateTimeZone('UTC'));
     $urls = array('https://a0.awsstatic.com/pricing/1/ec2/linux-od.min.js', 'https://a0.awsstatic.com/pricing/1/ec2/mswin-od.min.js');
     $mapping = array('us-east' => 'us-east-1', 'us-west' => 'us-west-1', 'us-west-2' => 'us-west-2', 'eu-ireland' => 'eu-west-1', 'sa-east-1' => 'sa-east-1', 'apac-sin' => 'ap-southeast-1', 'apac-tokyo' => 'ap-northeast-1', 'apac-syd' => 'ap-southeast-2');
     $availableLocations = Aws::getCloudLocations();
     foreach ($urls as $link) {
         $json = trim(preg_replace('/^.+?callback\\((.+?)\\);\\s*$/sU', '\\1', file_get_contents($link)));
         $data = json_decode(preg_replace('/(\\w+):/', '"\\1":', $json));
         if (!empty($data->config->regions)) {
             $cadb = Scalr::getContainer()->cadb;
             foreach ($data->config->regions as $rd) {
                 foreach ($rd->instanceTypes as $it) {
                     if (!isset($mapping[$rd->region])) {
                         throw new Exception(sprintf("Region %s does not exist in the mapping.", $rd->region));
                     }
                     $region = $mapping[$rd->region];
                     $latest = array();
                     //Gets latest prices for all instance types from current region.
                     $res = $cadb->Execute("\n                            SELECT p.instance_type, ph.applied, p.os, p.name, HEX(p.price_id) `price_id`, p.cost\n                            FROM price_history ph\n                            JOIN prices p ON p.price_id = ph.price_id\n                            LEFT JOIN price_history ph2 ON ph2.platform = ph.platform\n                                AND ph2.cloud_location = ph.cloud_location\n                                AND ph2.account_id = ph.account_id\n                                AND ph2.url = ph.url\n                                AND ph2.applied > ph.applied AND ph2.applied <= ?\n                            LEFT JOIN prices p2 ON p2.price_id = ph2.price_id\n                                AND p2.instance_type = p.instance_type\n                                AND p2.os = p.os\n                            WHERE ph.account_id = 0 AND p2.price_id IS NULL\n                            AND ph.platform = 'ec2'\n                            AND ph.cloud_location = ?\n                            AND ph.url = ''\n                            AND ph.applied <= ?\n                        ", array($now->format('Y-m-d'), $region, $now->format('Y-m-d')));
                     while ($rec = $res->FetchRow()) {
                         $latest[$rec['instance_type']][$rec['os']] = array('applied' => $rec['applied'], 'price_id' => $rec['price_id'], 'cost' => $rec['cost']);
                     }
                     $upd = array();
                     $needUpdate = false;
                     foreach ($it->sizes as $sz) {
                         foreach ($sz->valueColumns as $v) {
                             $os = $v->name == 'linux' ? PriceEntity::OS_LINUX : PriceEntity::OS_WINDOWS;
                             if (!isset($latest[$sz->size][$os])) {
                                 $needUpdate = true;
                             } else {
                                 if (abs(($latest[$sz->size][$os]['cost'] - $v->prices->USD) / $v->prices->USD) > 1.0E-6) {
                                     $needUpdate = true;
                                     $latest[$sz->size][$os]['cost'] = $v->prices->USD;
                                 } else {
                                     continue;
                                 }
                             }
                             $latest[$sz->size][$os] = array('cost' => $v->prices->USD);
                         }
                     }
                     if ($needUpdate) {
                         $priceid = $cadb->GetOne("\n                                SELECT HEX(`price_id`) AS `price_id`\n                                FROM price_history\n                                WHERE platform = 'ec2'\n                                AND url = ''\n                                AND cloud_location = ?\n                                AND applied = ?\n                                AND account_id = 0\n                                LIMIT 1\n                            ", array($region, $now->format('Y-m-d')));
                         if (!$priceid) {
                             $priceid = str_replace('-', '', Scalr::GenerateUID());
                             $cadb->Execute("\n                                    INSERT price_history\n                                    SET price_id = UNHEX(?),\n                                        platform = 'ec2',\n                                        url = '',\n                                        cloud_location = ?,\n                                        account_id = 0,\n                                        applied = ?,\n                                        deny_override = 0\n                                ", array($priceid, $region, $now->format('Y-m-d')));
                         }
                         foreach ($latest as $instanceType => $ld) {
                             foreach ($ld as $os => $v) {
                                 $cadb->Execute("\n                                        REPLACE prices\n                                        SET price_id = UNHEX(?),\n                                            instance_type = ?,\n                                            name = ?,\n                                            os = ?,\n                                            cost = ?\n                                    ", array($priceid, $instanceType, $instanceType, $os, $v['cost']));
                             }
                         }
                     }
                 }
             }
         }
     }
     exit;
 }
Пример #8
0
 /**
  * Gets quarter period object for the current date
  *
  * @return QuarterPeriod Returns quarter period object for the current date
  */
 private static function _getCurrentPeriod()
 {
     static $period;
     if ($period === null) {
         $quarters = new Quarters(SettingEntity::getQuarters());
         $period = $quarters->getPeriodForDate();
     }
     return $period;
 }
 /**
  * Saves project or cost center notification
  *
  * @param ProjectEntity|CostCentreEntity $subject       Project or cost center entity
  * @param NotificationEntity             $notification  Current notification object
  * @throws InvalidArgumentException
  */
 private function saveNotificationData($subject, NotificationEntity $notification)
 {
     $baseUrl = rtrim(\Scalr::getContainer()->config('scalr.endpoint.scheme') . "://" . \Scalr::getContainer()->config('scalr.endpoint.host'), '/');
     $quarters = new Quarters(SettingEntity::getQuarters());
     $date = $quarters->getPeriodForDate('yesterday');
     $formatedTitle = 'Q' . $quarters->getQuarterForDate('now') . ' budget (' . (new \DateTime('now', new \DateTimeZone('UTC')))->format('M j, Y') . ')';
     if ($subject instanceof ProjectEntity) {
         $getPeriodicSubjectData = 'getProjectPeriodData';
         $subjects = 'projects';
         $childItems = 'farms';
         $subjectIdName = 'projectId';
         $subjectName = 'project';
     } else {
         if ($subject instanceof CostCentreEntity) {
             $getPeriodicSubjectData = 'getCostCenterPeriodData';
             $subjects = 'costcenters';
             $childItems = 'projects';
             $subjectIdName = 'ccId';
             $subjectName = 'cc';
         } else {
             throw new InvalidArgumentException("Invalid subject parameter. It must be either ProjectEntity or CostCentreEntity type.");
         }
     }
     $periodSubjectData = \Scalr::getContainer()->analytics->usage->{$getPeriodicSubjectData}($subject->{$subjectIdName}, 'quarter', $date->start->format('Y-m-d'), $date->end->format('Y-m-d'));
     $subjectAnalytics = ['budget' => $periodSubjectData['totals']['budget'], 'name' => $subject->name, 'trends' => $periodSubjectData['totals']['trends'], 'forecastCost' => $periodSubjectData['totals']['forecastCost'], 'interval' => $periodSubjectData['interval'], 'date' => $formatedTitle, 'detailsUrl' => $baseUrl . '#/analytics/' . $subjects . '?' . $subjectIdName . '=' . $subject->{$subjectIdName}, 'jsonVersion' => '1.0.0', $childItems => []];
     if (!empty($periodSubjectData['totals'][$childItems])) {
         $subjectAnalytics[$childItems] = $this->getSubjectChildItems($subject, $periodSubjectData['totals'][$childItems], $date);
     }
     if ($notification->notificationType === NotificationEntity::NOTIFICATION_TYPE_USAGE) {
         $reportType = $subjectName . 'Usage';
         $budgetThreshold = 'budgetSpentPct';
         $emailSubject = $subjectAnalytics['name'] . ' usage notification.';
     } else {
         if ($notification->notificationType === NotificationEntity::NOTIFICATION_TYPE_PROJECTED_OVERSPEND) {
             $reportType = $subjectName . 'Overspend';
             $budgetThreshold = 'estimateOverspendPct';
             $emailSubject = $subjectAnalytics['name'] . ' overspend notification.';
         }
     }
     if ($subjectAnalytics['budget'][$budgetThreshold] >= $notification->threshold) {
         $subjectAnalytics['reportType'] = $reportType;
         $entity = ReportPayloadEntity::init([$notification->notificationType, $notification->subjectType, $subject->{$subjectIdName}, $notification->threshold], $subjectAnalytics);
         if (!ReportPayloadEntity::findPk($entity->uuid)) {
             $payload = json_decode($entity->payload, true);
             if (!empty($subjectAnalytics['budget']['estimateDate'])) {
                 $subjectAnalytics['budget']['estimateDate'] = (new DateTime($subjectAnalytics['budget']['estimateDate'], new DateTimeZone('UTC')))->format('M j, Y');
                 $subjectAnalytics['reportUrl'] = $payload['reportUrl'];
             }
             \Scalr::getContainer()->mailer->setSubject($emailSubject)->setContentType('text/html')->sendTemplate(SCALR_TEMPLATES_PATH . '/emails/budget_notification_' . $subjectName . '.html.php', $subjectAnalytics, $notification->emails);
             $this->getLogger()->info('Notification email has been sent');
             $payload['date'] = $entity->created->format('Y-m-d');
             $entity->payload = json_encode($payload);
             $entity->save();
         }
     }
 }
Пример #10
0
 /**
  * Gets project moving average to date
  *
  * @param   string|null $projectId    The identifier of the project
  * @param   string      $mode         The mode
  * @param   string      $date         The UTC date within period ('Y-m-d H:00')
  * @param   string      $startDate    The start date of the period in UTC ('Y-m-d')
  * @param   string      $endDate      The end date of the period in UTC ('Y-m-d')
  * @param   string      $ccId         optional The identifier of the cost center (It is used only when project is null)
  * @return  array       Returns project moving average to date
  * @throws  InvalidArgumentException
  * @throws  AnalyticsException
  */
 public function getProjectMovingAverageToDate($projectId, $mode, $date, $startDate, $endDate, $ccId = null)
 {
     $projectId = empty($projectId) ? null : $projectId;
     if (!preg_match('/^[\\d]{4}-[\\d]{2}-[\\d]{2} [\\d]{2}:00$/', $date)) {
         throw new InvalidArgumentException(sprintf("Invalid date:%s. 'YYYY-MM-DD HH:00' is expected.", strip_tags($date)));
     }
     if (!preg_match('/^[[:xdigit:]-]{36}$/', $projectId)) {
         throw new InvalidArgumentException(sprintf("Invalid identifier of the Project:%s. UUID is expected.", strip_tags($projectId)));
     }
     $iterator = ChartPeriodIterator::create($mode, $startDate, $endDate ?: null, 'UTC');
     //Interval which is used in the database query for grouping
     $queryInterval = preg_replace('/^1 /', '', $iterator->getInterval());
     if ($projectId !== null) {
         $project = ProjectEntity::findPk($projectId);
         if ($project === null) {
             if (empty($ccId)) {
                 throw new AnalyticsException(sprintf("Project %s does not exist. Please provide ccId.", $projectId));
             }
         } else {
             $ccId = $project->ccId;
         }
     }
     $data = $this->getRollingAvg(['projectId' => $projectId], $queryInterval, $date);
     $data['budgetUseToDate'] = null;
     $data['budgetUseToDatePct'] = null;
     //Does not calculate budget use to date for those cases
     if ($mode != 'custom' && $mode != 'year') {
         $pointPosition = $iterator->searchPoint($date);
         if ($pointPosition !== false) {
             $chartPoint = $iterator->current();
             //Gets the end date of the selected interval
             $end = clone $chartPoint->dt;
             if ($chartPoint->interval != '1 day') {
                 $end->modify("+" . $chartPoint->interval . " -1 day");
             }
             //Gets quarters config
             $quarters = new Quarters(SettingEntity::getQuarters());
             //Gets start and end of the quarter for the end date of the current interval
             $period = $quarters->getPeriodForDate($end);
             $data['year'] = $period->year;
             $data['quarter'] = $period->quarter;
             $data['quarterStartDate'] = $period->start->format('Y-m-d');
             $data['quarterEndDate'] = $period->end->format('Y-m-d');
             //Gets budgeted cost
             $budget = current(QuarterlyBudgetEntity::getProjectBudget($period->year, $projectId)->filterByQuarter($period->quarter));
             //If budget has not been set we should not calculate anything
             if ($budget instanceof QuarterlyBudgetEntity && round($budget->budget) > 0) {
                 $data['budget'] = round($budget->budget);
                 //Calculates usage from the start date of the quarter to date
                 $usage = $this->get(['projectId' => $projectId], $period->start, $end);
                 $data['budgetUseToDate'] = $usage['cost'];
                 $data['budgetUseToDatePct'] = $data['budget'] == 0 ? null : min(100, round($usage['cost'] / $data['budget'] * 100));
             }
         }
     }
     return ['data' => $data];
 }
Пример #11
0
 private function getFarmCostData($farmId)
 {
     $result = [];
     $costCenter = $this->getContainer()->analytics->ccs->get($this->getEnvironment()->getPlatformConfigValue(Scalr_Environment::SETTING_CC_ID));
     $currentYear = (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y');
     $quarters = new Quarters(SettingEntity::getQuarters());
     $currentQuarter = $quarters->getQuarterForDate(new \DateTime('now', new \DateTimeZone('UTC')));
     $projects = [];
     if ($farmId) {
         $farm = DBFarm::LoadByID($farmId);
         $currentProjectId = $farm->GetSetting(Entity\FarmSetting::PROJECT_ID);
         $currentProject = ProjectEntity::findPk($currentProjectId);
         /* @var $currentProject ProjectEntity */
         if (!empty($currentProject)) {
             $quarterBudget = QuarterlyBudgetEntity::findOne([['year' => $currentYear], ['subjectType' => QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT], ['subjectId' => $currentProject->projectId], ['quarter' => $currentQuarter]]);
             $projects[] = ['projectId' => $currentProject->projectId, 'name' => "{$currentProject->name} / {$currentProject->getCostCenter()->name}", 'budgetRemain' => !is_null($quarterBudget) && $quarterBudget->budget > 0 ? max(0, round($quarterBudget->budget - $quarterBudget->cumulativespend)) : null];
         }
         $result['projectId'] = $farm->GetSetting(Entity\FarmSetting::PROJECT_ID);
         $result['farmCostMetering'] = $result['projectId'] ? $this->getContainer()->analytics->usage->getFarmCostMetering($this->user->getAccountId(), $farmId) : null;
     }
     if ($costCenter instanceof CostCentreEntity) {
         $projectsIterator = new SharedProjectsFilterIterator($costCenter->getProjects(), $costCenter->ccId, $this->user, $this->getEnvironment());
         foreach ($projectsIterator as $item) {
             /* @var $item Scalr\Stats\CostAnalytics\Entity\ProjectEntity */
             if (!empty($currentProjectId) && $item->projectId == $currentProjectId) {
                 continue;
             }
             $quarterBudget = QuarterlyBudgetEntity::findOne([['year' => $currentYear], ['subjectType' => QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT], ['subjectId' => $item->projectId], ['quarter' => $currentQuarter]]);
             $projects[] = array('projectId' => $item->projectId, 'name' => "{$item->name} / {$costCenter->name}", 'budgetRemain' => !is_null($quarterBudget) && $quarterBudget->budget > 0 ? max(0, round($quarterBudget->budget - $quarterBudget->cumulativespend)) : null);
         }
         $costCentreName = $costCenter->name;
         $isLocked = $costCenter->getProperty(CostCentrePropertyEntity::NAME_LOCKED);
         $accountCcs = AccountCostCenterEntity::findOne([['accountId' => $this->environment->clientId], ['ccId' => $costCenter->ccId]]);
         if ($isLocked || !$accountCcs instanceof AccountCostCenterEntity) {
             $costCentreLocked = 1;
         } else {
             $costCentreLocked = 0;
         }
     } else {
         $costCentreName = '';
         $costCentreLocked = 0;
     }
     $supportedClouds = $this->getContainer()->analytics->prices->getSupportedClouds();
     $result['analytics'] = array('costCenterName' => $costCentreName, 'costCenterLocked' => $costCentreLocked, 'projects' => $projects, 'unsupportedClouds' => array_values(array_diff($this->environment->getEnabledPlatforms(), $supportedClouds)));
     return $result;
 }
Пример #12
0
 /**
  * Returns iterator for current quarter
  *
  * @return Iterator\ChartQuarterlyIterator
  */
 public function getCurrentQuarterIterator()
 {
     $quarters = new Quarters(SettingEntity::getQuarters());
     $currentQuarter = $quarters->getQuarterForDate(new \DateTime('now', new \DateTimeZone('UTC')));
     $currentYear = (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y');
     if ($currentQuarter === 1) {
         $quarter = 4;
         $year = $currentYear - 1;
     } else {
         $quarter = $currentQuarter - 1;
         $year = $currentYear;
     }
     $date = $quarters->getPeriodForQuarter($quarter, $year);
     $iterator = ChartPeriodIterator::create('quarter', $date->start, $date->end, 'UTC');
     return $iterator;
 }
Пример #13
0
 protected function run5($stage)
 {
     $this->console->out("Updating reports status...");
     $this->db->Execute("UPDATE `reports` set `status` = ?", array((int) SettingEntity::getValue(SettingEntity::ID_REPORTS_ENABLED)));
 }
Пример #14
0
 /**
  * {@inheritdoc}
  * @see Iterator::next()
  */
 public function next()
 {
     if ($this->interval == '1 week') {
         $this->dt->modify('next sunday');
     } elseif ($this->interval == '1 quarter') {
         //First day of next quarter
         $quarters = new Quarters(SettingEntity::getQuarters());
         $period = $quarters->getPeriodForDate($this->dt);
         $this->dt = $period->end;
         $this->dt->modify('+1 day');
     } else {
         $this->dt->add($this->di);
     }
     $this->i++;
 }
 /**
  * {@inheritdoc}
  * @see \Scalr\System\Pcntl\ProcessInterface::OnStartForking()
  */
 public function OnStartForking()
 {
     if (!\Scalr::getContainer()->analytics->enabled) {
         die("Terminating the process as Cost analytics is disabled in the config.\n");
     }
     $this->console->out("%s (UTC) Start Analytics Notifications process", gmdate('Y-m-d'));
     if (SettingEntity::getValue(SettingEntity::ID_NOTIFICATIONS_CCS_ENABLED) || SettingEntity::getValue(SettingEntity::ID_NOTIFICATIONS_PROJECTS_ENABLED)) {
         $this->console->out('Calculating data for projects notifications');
         $quarters = new Quarters(SettingEntity::getQuarters());
         $date = $quarters->getPeriodForDate('yesterday');
         $formatedTitle = 'Q' . $quarters->getQuarterForDate('now') . ' budget (' . (new \DateTime('now', new \DateTimeZone('UTC')))->format('M j, Y') . ')';
         $projects = ProjectEntity::find();
         foreach ($projects as $project) {
             $periodProjectData = \Scalr::getContainer()->analytics->usage->getProjectPeriodData($project->projectId, 'quarter', $date->start->format('Y-m-d'), $date->end->format('Y-m-d'));
             $projectAnalytics[$project->projectId] = ['budget' => $periodProjectData['totals']['budget'], 'name' => $project->name, 'trends' => $periodProjectData['totals']['trends'], 'forecastCost' => $periodProjectData['totals']['forecastCost'], 'interval' => $periodProjectData['interval'], 'jsonVersion' => '1.0.0', 'farms' => []];
             if (!empty($periodProjectData['totals']['farms'])) {
                 foreach ($periodProjectData['totals']['farms'] as $farm) {
                     $projectAnalytics[$project->projectId]['farms'][] = ['id' => $farm['id'], 'name' => $farm['name'], 'median' => $farm['median'] / 7, 'cost' => $farm['cost'], 'costPct' => $farm['costPct']];
                 }
                 if (count($projectAnalytics[$project->projectId]['farms'] > 1)) {
                     usort($projectAnalytics[$project->projectId]['farms'], array($this, 'sortItems'));
                     if (count($projectAnalytics[$project->projectId]['farms'] > 6)) {
                         array_splice($projectAnalytics[$project->projectId]['farms'], 6, count($projectAnalytics[$project->projectId]['farms']));
                     }
                 }
             }
             $projectAnalytics[$project->projectId]['date'] = $formatedTitle;
             if ($project->archived) {
                 continue;
             }
             if (SettingEntity::getValue(SettingEntity::ID_NOTIFICATIONS_PROJECTS_ENABLED)) {
                 $projectNotifications = NotificationEntity::findBySubjectType(NotificationEntity::SUBJECT_TYPE_PROJECT);
                 foreach ($projectNotifications as $notification) {
                     $this->saveNotificationData('project', $notification, $project->projectId, $projectAnalytics);
                 }
             }
         }
     }
     if (SettingEntity::getValue(SettingEntity::ID_NOTIFICATIONS_CCS_ENABLED)) {
         $this->console->out('Calculating data for cost center notifications');
         $ccs = CostCentreEntity::find();
         foreach ($ccs as $cc) {
             if ($cc->archived) {
                 continue;
             }
             $periodCostCenterData = \Scalr::getContainer()->analytics->usage->getCostCenterPeriodData($cc->ccId, 'quarter', $date->start->format('Y-m-d'), $date->end->format('Y-m-d'));
             $ccAnalytics[$cc->ccId] = ['budget' => $periodCostCenterData['totals']['budget'], 'name' => $cc->name, 'trends' => $periodCostCenterData['totals']['trends'], 'forecastCost' => $periodCostCenterData['totals']['forecastCost'], 'interval' => $periodCostCenterData['interval'], 'jsonVersion' => '1.0.0', 'projects' => []];
             if (!empty($periodCostCenterData['totals']['projects'])) {
                 foreach ($periodCostCenterData['totals']['projects'] as $key => $project) {
                     if (!empty($project['id'])) {
                         $projectBudget = $projectAnalytics[$project['id']]['budget'];
                         $projectBudget['name'] = $project['name'];
                         $projectBudget['id'] = $project['id'];
                         $projectBudget['median'] = $project['median'] / 7;
                         $ccAnalytics[$cc->ccId]['projects'][] = $projectBudget;
                     } else {
                         $otherProjectsKey = $key;
                     }
                 }
                 if (isset($otherProjectsKey)) {
                     $ccAnalytics[$cc->ccId]['projects'][] = ['id' => '', 'budgetSpent' => $periodCostCenterData['totals']['projects'][$otherProjectsKey]['cost'], 'median' => $periodCostCenterData['totals']['projects'][$otherProjectsKey]['median'] / 7, 'name' => $periodCostCenterData['totals']['projects'][$otherProjectsKey]['name'], 'estimateOverspend' => null];
                     unset($otherProjectsKey);
                 }
                 if (count($ccAnalytics[$cc->ccId]['projects'] > 1)) {
                     usort($ccAnalytics[$cc->ccId]['projects'], array($this, 'sortItems'));
                     if (count($ccAnalytics[$cc->ccId]['projects'] > 6)) {
                         array_splice($ccAnalytics[$cc->ccId]['projects'], 6, count($ccAnalytics[$cc->ccId]['projects']));
                     }
                 }
             }
             $ccAnalytics[$cc->ccId]['date'] = $formatedTitle;
             $ccsNotifications = NotificationEntity::findBySubjectType(NotificationEntity::SUBJECT_TYPE_CC);
             foreach ($ccsNotifications as $notification) {
                 $this->saveNotificationData('cc', $notification, $cc->ccId, $ccAnalytics);
             }
         }
     }
     if (SettingEntity::getValue(SettingEntity::ID_REPORTS_ENABLED)) {
         $this->console->out('Calculating data for reports');
         $reports = ReportEntity::find();
         foreach ($reports as $report) {
             switch ($report->period) {
                 case ReportEntity::PERIOD_DAILY:
                     $period = 'custom';
                     $start = (new \DateTime('yesterday', new \DateTimeZone('UTC')))->format('Y-m-d');
                     $end = $start;
                     $startForecast = (new \DateTime('first day of this month', new \DateTimeZone('UTC')))->format('Y-m-d');
                     $endForecast = (new \DateTime('last day of this month', new \DateTimeZone('UTC')))->format('Y-m-d');
                     if ($startForecast == (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y-m-d')) {
                         $startForecast = (new \DateTime('first day of last month', new \DateTimeZone('UTC')))->format('Y-m-d');
                         $endForecast = (new \DateTime('last day of last month', new \DateTimeZone('UTC')))->format('Y-m-d');
                     }
                     $periodForecast = 'month';
                     $formatedTitle = (new \DateTime($start, new \DateTimeZone('UTC')))->format('M j');
                     break;
                 case ReportEntity::PERIOD_MONTHLY:
                     $period = 'month';
                     $start = (new \DateTime('first day of last month', new \DateTimeZone('UTC')))->format('Y-m-d');
                     $end = (new \DateTime('last day of last month', new \DateTimeZone('UTC')))->format('Y-m-d');
                     $quarters = new Quarters(SettingEntity::getQuarters());
                     $currentQuarter = $quarters->getQuarterForDate(new \DateTime($start, new \DateTimeZone('UTC')));
                     $currentYear = (new \DateTime($start, new \DateTimeZone('UTC')))->format('Y');
                     $date = $quarters->getPeriodForQuarter($currentQuarter, $currentYear);
                     $formatedTitle = (new \DateTime($start, new \DateTimeZone('UTC')))->format('M Y');
                     $startForecast = $date->start->format('Y-m-d');
                     $endForecast = $date->end->format('Y-m-d');
                     $periodForecast = 'quarter';
                     break;
                 case ReportEntity::PERIOD_QUARTELY:
                     $period = 'quarter';
                     $quarters = new Quarters(SettingEntity::getQuarters());
                     $currentQuarter = $quarters->getQuarterForDate(new \DateTime('yesterday', new \DateTimeZone('UTC')));
                     $currentYear = (new \DateTime('yesterday', new \DateTimeZone('UTC')))->format('Y');
                     if ($currentQuarter === 1) {
                         $quarter = 4;
                         $year = $currentYear - 1;
                     } else {
                         $quarter = $currentQuarter - 1;
                         $year = $currentYear;
                     }
                     $date = $quarters->getPeriodForQuarter($quarter, $year);
                     $start = $date->start->format('Y-m-d');
                     $end = $date->end->format('Y-m-d');
                     $formatedTitle = 'Q' . $quarter . ' ' . $year;
                     $startForecast = $currentYear . '-01-01';
                     $endForecast = $currentYear . '-12-31';
                     $periodForecast = 'year';
                     break;
                 case ReportEntity::PERIOD_WEEKLY:
                     $period = 'week';
                     $end = (new \DateTime('yesterday', new \DateTimeZone('UTC')))->modify('last saturday')->format('Y-m-d');
                     $start = (new \DateTime($end, new \DateTimeZone('UTC')))->modify('last sunday')->format('Y-m-d');
                     $formatedTitle = (new \DateTime($start, new \DateTimeZone('UTC')))->format('M j') . ' - ' . (new \DateTime($end, new \DateTimeZone('UTC')))->format('M j');
                     $startForecast = (new \DateTime('first day of this month', new \DateTimeZone('UTC')))->format('Y-m-d');
                     $endForecast = (new \DateTime('last day of this month', new \DateTimeZone('UTC')))->format('Y-m-d');
                     if ($startForecast == (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y-m-d')) {
                         $startForecast = (new \DateTime('first day of last month', new \DateTimeZone('UTC')))->format('Y-m-d');
                         $endForecast = (new \DateTime('last day of last month', new \DateTimeZone('UTC')))->format('Y-m-d');
                     }
                     $periodForecast = 'month';
                     break;
             }
             if ($report->subjectType === ReportEntity::SUBJECT_TYPE_CC) {
                 $getPeriodicSubjectData = 'getCostCenterPeriodData';
                 $subjectEntityName = 'Scalr\\Stats\\CostAnalytics\\Entity\\CostCentre';
                 $subjectId = 'ccId';
             } else {
                 if ($report->subjectType === ReportEntity::SUBJECT_TYPE_PROJECT) {
                     $getPeriodicSubjectData = 'getProjectPeriodData';
                     $subjectEntityName = 'Scalr\\Stats\\CostAnalytics\\Entity\\Project';
                     $subjectId = 'projectId';
                 } else {
                     $periodData = \Scalr::getContainer()->analytics->usage->getDashboardPeriodData($period, $start, $end);
                     $periodDataForecast = \Scalr::getContainer()->analytics->usage->getDashboardPeriodData($periodForecast, $startForecast, $endForecast);
                     $periodData['period'] = $period;
                     $periodData['forecastPeriod'] = $periodForecast;
                     $periodData['totals']['forecastCost'] = $periodDataForecast['totals']['forecastCost'];
                     $periodData['totals']['trends'] = $periodDataForecast['totals']['trends'];
                     $periodData['name'] = 'Cloud Cost Report';
                     $periodData['jsonVersion'] = '1.0.0';
                     $periodData['totals']['clouds'] = $this->changeCloudNames($periodData['totals']['clouds']);
                     $periodData['date'] = $formatedTitle;
                     $periodData['totals']['budget']['budget'] = null;
                     if ($period == 'quarter') {
                         $periodData['totals']['budget'] = ['quarter' => $quarter, 'year' => $year, 'quarterStartDate' => $start, 'quarterEndDate' => $end];
                     } else {
                         if ($period == 'month') {
                             $periodData['totals']['budget'] = ['quarter' => $currentQuarter];
                         }
                     }
                     unset($periodData['projects'], $periodData['budget']['projects']);
                     if (count($periodData['costcenters'] > 1)) {
                         uasort($periodData['costcenters'], array($this, 'sortItems'));
                         if (count($periodData['costcenters'] > 6)) {
                             array_splice($periodData['costcenters'], 6, count($periodData['costcenters']));
                         }
                     }
                     if (count($periodData['totals']['clouds'] > 1)) {
                         usort($periodData['totals']['clouds'], array($this, 'sortItems'));
                     }
                     $entity = ReportPayloadEntity::init([$report->subjectType, $report->subjectId, $period], $periodData, $start);
                     if (!ReportPayloadEntity::findPk($entity->uuid)) {
                         $payload = json_decode($entity->payload, true);
                         \Scalr::getContainer()->mailer->setSubject('Summary report.')->setContentType('text/html')->sendTemplate(SCALR_TEMPLATES_PATH . '/emails/report_summary.html.php', $payload, $report->emails);
                         $this->console->out('Summary report email has been sent');
                         $payload['date'] = $entity->created->format('Y-m-d');
                         $entity->payload = json_encode($payload);
                         $entity->save();
                     }
                 }
             }
             unset($currentQuarter, $currentYear);
             if (!empty($report->subjectType) && !empty($report->subjectId)) {
                 $subject = call_user_func($subjectEntityName . 'Entity::findPk', $report->subjectId);
                 if ($subject->archived) {
                     continue;
                 }
                 $this->saveReportData($getPeriodicSubjectData, $subjectEntityName, ['period' => $period, 'start' => $start, 'end' => $end], ['period' => $periodForecast, 'start' => $startForecast, 'end' => $endForecast], $report->subjectId, $report->subjectType, $report->emails, $formatedTitle);
             } else {
                 if (!empty($report->subjectType)) {
                     $subjects = call_user_func($subjectEntityName . 'Entity::find');
                     foreach ($subjects as $subject) {
                         if ($subject->archived) {
                             continue;
                         }
                         $this->saveReportData($getPeriodicSubjectData, $subjectEntityName, ['period' => $period, 'start' => $start, 'end' => $end], ['period' => $periodForecast, 'start' => $startForecast, 'end' => $endForecast], $subject->{$subjectId}, $report->subjectType, $report->emails, $formatedTitle);
                     }
                 }
             }
         }
     }
     $this->console->out('Done');
     exit;
 }
Пример #16
0
 /**
  * Constructor
  *
  * @param   ChartPeriodIterator $iterator  The iterator
  * @throws  \InvalidArgumentException
  */
 public function __construct(ChartPeriodIterator $iterator)
 {
     $this->mode = $iterator->getMode();
     $this->dt = $iterator->getIterationTimestamp();
     $this->interval = $iterator->getInterval();
     $this->i = $iterator->getIterationNumber();
     $prevStart = $iterator->getPreviousStart();
     $previousPeriodDt = clone $this->dt;
     $previousPeriodDt->sub($iterator->getPreviousPeriodInterval());
     $this->start = $iterator->getStart();
     $this->end = $iterator->getEnd();
     $this->isLastPoint = $iterator->isLastPoint();
     if ($this->mode == 'year' || $this->mode == 'custom' && $this->interval == '1 month') {
         $this->show = $this->label = $this->dt->format('M');
         $this->key = $this->dt->format('Y-m');
         $this->previousPeriodKey = $previousPeriodDt->format('Y-m');
     } elseif ($this->mode == 'quarter' || $this->mode == 'custom' && $this->interval == '1 week') {
         $ddt = clone $this->dt;
         $ddt->modify('next saturday');
         if ($ddt > $this->end) {
             $ddt = $this->end;
         }
         $this->label = $this->dt->format('M j') . ' - ' . $ddt->format('M j');
         $this->key = \Scalr_Util_DateTime::yearweek($this->dt->format('Y-m-d'));
         $this->previousPeriodKey = \Scalr_Util_DateTime::yearweek($previousPeriodDt->format('Y-m-d'));
         $this->show = $this->i % 3 == 0 ? $this->dt->format('M j') : ($this->isLastPoint && $this->i % 3 > 1 ? $ddt->format('M j') : '');
     } elseif ($this->mode == 'week') {
         $this->label = $this->dt->format('l, M j');
         $this->show = $this->dt->format('M j');
         $this->key = $this->dt->format('Y-m-d');
         $this->previousPeriodKey = $previousPeriodDt->format('Y-m-d');
     } elseif ($this->mode == 'month' || $this->mode == 'custom' && $this->interval == '1 day') {
         $this->label = $this->dt->format('M j');
         $this->key = $this->dt->format('Y-m-d');
         $this->previousPeriodKey = $previousPeriodDt->format('Y-m-d');
         $this->show = $this->i % 4 == 0 || $this->isLastPoint && $this->i % 4 > 2 ? $this->dt->format('M j') : '';
     } elseif ($this->mode == 'custom') {
         switch ($this->interval) {
             case '1 hour':
                 $h = $this->dt->format('H');
                 $this->label = $this->dt->format('l, M j, g A');
                 $this->show = $h == 0 ? $this->dt->format('M j') : ($h % 3 == 0 ? $this->dt->format('g a') : '');
                 $this->key = $this->dt->format("Y-m-d H:00:00");
                 $this->previousPeriodKey = $previousPeriodDt->format('Y-m-d H:00:00');
                 break;
             case '1 quarter':
                 //Quarter breakdown is not supported yet
                 $quarters = new Quarters(SettingEntity::getQuarters());
                 $currentPeriod = $quarters->getPeriodForDate($this->start);
                 $prevPeriod = $quarters->getPeriodForDate($prevStart);
                 $this->show = $this->label = $currentPeriod->year . ' Q' . $currentPeriod->quarter;
                 $this->key = $currentPeriod->year . '-' . $currentPeriod->quarter;
                 $this->previousPeriodKey = $prevPeriod->year . '-' . $prevPeriod->quarter;
                 break;
             case '1 year':
                 $this->show = $this->label = $this->dt->format('Y');
                 $this->key = $this->label;
                 $this->previousPeriodKey = $previousPeriodDt->format('Y');
                 break;
             default:
                 throw new \InvalidArgumentException(sprintf('Unsupported interval for custom mode %s.', $this->interval));
                 break;
         }
     } else {
         throw new \InvalidArgumentException(sprintf('Invalid mode %s', strip_tags($this->mode)));
     }
 }
Пример #17
0
 /**
  * Default action
  */
 public function defaultAction()
 {
     $this->response->page('ui/analytics/dashboard/view.js', ['quarters' => SettingEntity::getQuarters(true), 'envName' => $this->environment->name, 'ccName' => $this->getContainer()->analytics->ccs->get($this->environment->getPlatformConfigValue(Scalr_Environment::SETTING_CC_ID))->name], ['/ui/analytics/analytics.js'], ['ui/analytics/analytics.css', 'ui/admin/analytics/admin.css']);
 }
Пример #18
0
 public function xSaveAction()
 {
     $this->request->defineParams(array('projectId' => ['type' => 'string'], 'year' => ['type' => 'int'], 'quarters' => ['type' => 'json'], 'selectedQuarter' => ['type' => 'string']));
     $year = $this->getParam('year');
     $selectedQuarter = $this->getParam('selectedQuarter');
     if ($selectedQuarter !== 'year' && ($selectedQuarter < 1 || $selectedQuarter > 4)) {
         throw new OutOfBoundsException(sprintf("Invalid selectedQuarter number."));
     }
     $quarterReq = [];
     foreach ($this->getParam('quarters') as $q) {
         if (!isset($q['quarter'])) {
             throw new InvalidArgumentException(sprintf("Missing quarter property for quarters data set in the request."));
         }
         if ($q['quarter'] < 1 || $q['quarter'] > 4) {
             throw new OutOfRangeException(sprintf("Quarter value should be between 1 and 4."));
         }
         if (!isset($q['budget'])) {
             throw new InvalidArgumentException(sprintf("Missing budget property for quarters data set in the request."));
         }
         $quarterReq[$q['quarter']] = $q;
     }
     if ($this->getParam('projectId')) {
         $subjectType = QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT;
         $subjectId = $this->getParam('projectId');
         $subjectEntity = ProjectEntity::findPk($subjectId);
         /* @var $subjectEntity ProjectEntity */
         if ($subjectEntity->accountId != $this->user->getAccountId() || $subjectEntity->shared != ProjectEntity::SHARED_WITHIN_ACCOUNT) {
             throw new Scalr_Exception_InsufficientPermissions();
         }
     } else {
         throw new InvalidArgumentException(sprintf('ProjectId must be provided with the request.'));
     }
     if (!preg_match("/^[[:xdigit:]-]{36}\$/", $subjectId)) {
         throw new InvalidArgumentException(sprintf("Invalid UUID has been passed."));
     }
     if (!preg_match('/^\\d{4}$/', $year)) {
         throw new InvalidArgumentException(sprintf("Invalid year has been passed."));
     }
     //Fetches the previous state of the entities from database
     $collection = QuarterlyBudgetEntity::getProjectBudget($year, $subjectId);
     $quarters = new Quarters(SettingEntity::getQuarters(true));
     //Updates|creates entities
     for ($quarter = 1; $quarter <= 4; ++$quarter) {
         if (!isset($quarterReq[$quarter])) {
             continue;
         }
         $period = $quarters->getPeriodForQuarter($quarter, $year);
         //Checks if period has already been closed and forbids update
         if ($period->end->format('Y-m-d') < gmdate('Y-m-d')) {
             continue;
         }
         $entity = current($collection->filterByQuarter($quarter));
         if ($entity instanceof QuarterlyBudgetEntity) {
             //We should update an entity
             $entity->budget = abs((double) $quarterReq[$quarter]['budget']);
         } else {
             //We should create a new one.
             $entity = new QuarterlyBudgetEntity($year, $quarter);
             $entity->subjectType = $subjectType;
             $entity->subjectId = $subjectId;
             $entity->budget = abs((double) $quarterReq[$quarter]['budget']);
         }
         $entity->save();
     }
     if ($selectedQuarter == 'year') {
         $selectedPeriod = $quarters->getPeriodForYear($year);
     } else {
         $selectedPeriod = $quarters->getPeriodForQuarter($selectedQuarter, $year);
     }
     $data = $this->getProjectData(ProjectEntity::findPk($subjectId), $selectedPeriod, true);
     $budgetInfo = $this->getBudgetInfo($year, $data['ccId'], $data['projectId']);
     $this->response->data(['data' => $data, 'budgetInfo' => $budgetInfo]);
     $this->response->success('Budget changes have been saved');
 }
Пример #19
0
 /**
  * {@inheritdoc}
  * @see Iterator::current()
  * @return ChartPointInfo
  */
 public function current()
 {
     if (!isset($this->c[$this->i])) {
         $chartPoint = new ChartPointInfo($this);
         $previousPeriodDt = clone $chartPoint->dt;
         $previousPeriodDt->sub($this->getPreviousPeriodInterval());
         switch ($chartPoint->interval) {
             case '1 hour':
                 $h = $chartPoint->dt->format('H');
                 $chartPoint->label = $chartPoint->dt->format('l, M j, g A');
                 $chartPoint->show = $h == 0 ? $chartPoint->dt->format('M j') : ($h % 3 == 0 ? $chartPoint->dt->format('g a') : '');
                 $chartPoint->key = $chartPoint->dt->format("Y-m-d H:00:00");
                 $chartPoint->previousPeriodKey = $previousPeriodDt->format('Y-m-d H:00:00');
                 break;
             case '1 day':
                 $chartPoint->label = $chartPoint->dt->format('M j');
                 $chartPoint->key = $chartPoint->dt->format('Y-m-d');
                 $chartPoint->previousPeriodKey = $previousPeriodDt->format('Y-m-d');
                 $chartPoint->show = $chartPoint->i % 4 == 0 || $chartPoint->isLastPoint && $chartPoint->i % 4 > 2 ? $chartPoint->dt->format('M j') : '';
                 break;
             case '1 week':
                 $ddt = clone $chartPoint->dt;
                 $ddt->modify('next saturday');
                 if ($ddt > $chartPoint->end) {
                     $ddt = $chartPoint->end;
                 }
                 $chartPoint->label = $chartPoint->dt->format('M j') . ' - ' . $ddt->format('M j');
                 $chartPoint->key = \Scalr_Util_DateTime::yearweek($chartPoint->dt->format('Y-m-d'));
                 $chartPoint->previousPeriodKey = \Scalr_Util_DateTime::yearweek($previousPeriodDt->format('Y-m-d'));
                 $chartPoint->show = $chartPoint->i % 3 == 0 ? $chartPoint->dt->format('M j') : ($chartPoint->isLastPoint && $chartPoint->i % 3 > 1 ? $ddt->format('M j') : '');
                 break;
             case '1 month':
                 $diffdays = $this->start->diff($this->end, true)->days;
                 if ($diffdays < 366) {
                     $chartPoint->show = $chartPoint->label = $chartPoint->dt->format('M');
                 } else {
                     $chartPoint->show = $chartPoint->label = $chartPoint->dt->format('M, Y');
                 }
                 $chartPoint->key = $chartPoint->dt->format('Y-m');
                 $chartPoint->previousPeriodKey = $previousPeriodDt->format('Y-m');
                 break;
             case '1 quarter':
                 //Quarter breakdown is not supported yet
                 $quarters = new Quarters(SettingEntity::getQuarters());
                 $currentPeriod = $quarters->getPeriodForDate($chartPoint->start);
                 $prevStart = $this->getPreviousStart();
                 $prevPeriod = $quarters->getPeriodForDate($prevStart);
                 $chartPoint->show = $chartPoint->label = $currentPeriod->year . ' Q' . $currentPeriod->quarter;
                 $chartPoint->key = $currentPeriod->year . '-' . $currentPeriod->quarter;
                 $chartPoint->previousPeriodKey = $prevPeriod->year . '-' . $prevPeriod->quarter;
                 break;
             case '1 year':
                 $chartPoint->show = $chartPoint->label = $chartPoint->dt->format('Y');
                 $chartPoint->key = $chartPoint->label;
                 $chartPoint->previousPeriodKey = $previousPeriodDt->format('Y');
                 break;
             default:
                 throw new \InvalidArgumentException(sprintf('Unsupported interval for custom mode %s.', $chartPoint->interval));
                 break;
         }
         $this->c[$this->i] = $chartPoint;
     }
     return $this->c[$this->i];
 }
Пример #20
0
 public function defaultAction()
 {
     $this->response->page('ui/admin/analytics/costcenters/view.js', array('ccs' => $this->getCostCentersList(), 'quarters' => SettingEntity::getQuarters(true)), array('/ui/analytics/analytics.js'), array('ui/analytics/analytics.css', '/ui/admin/analytics/admin.css'));
 }
Пример #21
0
 /**
  * {@inheritdoc}
  * @see \Scalr\System\Zmq\Cron\AbstractTask::enqueue
  */
 public function enqueue()
 {
     $logger = $this->getLogger();
     if (!\Scalr::getContainer()->analytics->enabled) {
         $logger->info("CA has not been enabled in config!\n");
     }
     $db = \Scalr::getDb();
     $cadb = \Scalr::getContainer()->cadb;
     $pricing = new stdPricing();
     $quarters = new Quarters(SettingEntity::getQuarters());
     $logger->info('Started AnalyticsDemo process');
     $tzUtc = new DateTimeZone('UTC');
     /* @var $projects stdProject[] */
     $projects = [];
     /* @var $ccs stdCc[] */
     $ccs = [];
     /* @var $farms stdFarm[] */
     $farms = [];
     /* @var $environments stdEnv[] */
     $environments = [];
     /* @var $farmRoles stdFarmRole[] */
     //$farmRoles = [];
     //Analytics container
     $analytics = \Scalr::getContainer()->analytics;
     $logger->debug('CC & PROJECTS ---');
     foreach ($analytics->ccs->all(true) as $cc) {
         /* @var $cc \Scalr\Stats\CostAnalytics\Entity\CostCentreEntity */
         $co = new stdCc();
         $co->cc = $cc;
         $ccs[$cc->ccId] = $co;
         $logger->debug("Cost center: '%s'", $cc->name);
         foreach ($cc->getProjects() as $project) {
             /* @var $project \Scalr\Stats\CostAnalytics\Entity\ProjectEntity */
             $project->loadProperties();
             $po = new stdProject();
             $po->project = $project;
             $po->cc = $co;
             $projects[$project->projectId] = $po;
             $logger->debug("-- Project: '%s'", $project->name);
         }
     }
     //Ordering cost centers
     $number = 0;
     foreach ($ccs as $obj) {
         $obj->number = $number++;
     }
     //Ordering projects
     $number = 0;
     foreach ($projects as $obj) {
         $obj->number = $number++;
     }
     $logger->debug("FARMS ---");
     $pastIterations = static::PAST_HOURS_INIT;
     //Current time
     $start = new DateTime('now', $tzUtc);
     $dt = clone $start;
     do {
         $timestamp = $dt->format('Y-m-d H:00:00');
         $period = $quarters->getPeriodForDate($dt->format('Y-m-d'));
         $logger->info("Processing time:%s, year:%d, quarter:%d", $timestamp, $period->year, $period->quarter);
         //Gets farms for each project
         foreach ($projects as $po) {
             foreach ($analytics->projects->getFarmsList($po->project->projectId) as $farmId => $farmName) {
                 if (!isset($farms[$farmId])) {
                     $fo = new stdFarm();
                     $fo->farm = \DBFarm::LoadByID($farmId);
                     $fo->project = $po;
                     $fo->cc = $po->cc;
                     //$po->farms[] = $fo;
                     $farms[$farmId] = $fo;
                     if (!isset($environments[$fo->farm->EnvID])) {
                         $eo = new stdEnv();
                         $eo->env = $fo->farm->getEnvironmentObject();
                         //$eo->farms = [$farmId => $fo];
                         $environments[$fo->farm->EnvID] = $eo;
                         $fo->env = $eo;
                     } else {
                         //$environments[$fo->farm->EnvID]->farms[$farmId] = $fo;
                         $fo->env = $environments[$fo->farm->EnvID];
                     }
                     $fo->farmRoles = [];
                     foreach ($fo->farm->GetFarmRoles() as $farmRole) {
                         $fro = new stdFarmRole();
                         $fro->farmRole = $farmRole;
                         $fro->farm = $fo;
                         $fro->min = $farmRole->GetSetting(\DBFarmRole::SETTING_SCALING_MIN_INSTANCES);
                         $fro->max = $farmRole->GetSetting(\DBFarmRole::SETTING_SCALING_MAX_INSTANCES);
                         $fo->farmRoles[$farmRole->ID] = $fro;
                         //$farmRoles[$farmRole->ID] = $fro;
                     }
                 } else {
                     $fo = $farms[$farmId];
                 }
                 $logger->debug("Farm:'%s':%d from Env:'%s':%d corresponds to Project:'%s' -> CC:'%s'", $fo->farm->Name, $fo->farm->ID, $fo->farm->getEnvironmentObject()->name, $fo->farm->EnvID, $po->project->name, $po->cc->cc->name);
                 foreach ($fo->farmRoles as $fro) {
                     /* @var $fro stdFarmRole */
                     $countInstances = rand(max(1, floor($fro->max * 0.7)), min((int) $fro->max, 2));
                     $cost = $pricing->getPrice($dt, $fro->farmRole->Platform, $fro->farmRole->CloudLocation, $fro->getInstanceType(), $fo->env->getUrl($fro->farmRole->Platform), PriceEntity::OS_LINUX);
                     $type = UsageTypeEntity::NAME_COMPUTE_BOX_USAGE;
                     $costDistType = UsageTypeEntity::COST_DISTR_TYPE_COMPUTE;
                     $usageTypeEntity = UsageTypeEntity::findOne([['costDistrType' => $costDistType], ['name' => $type]]);
                     /* @var $usageTypeEntity UsageTypeEntity */
                     if ($usageTypeEntity === null) {
                         $usageTypeEntity = new UsageTypeEntity();
                         $usageTypeEntity->costDistrType = $costDistType;
                         $usageTypeEntity->name = $type;
                         $usageTypeEntity->displayName = 'Compute instances';
                         $usageTypeEntity->save();
                     }
                     $item = $fro->getInstanceType();
                     $usageItemEntity = UsageItemEntity::findOne([['usageType' => $usageTypeEntity->id], ['name' => $item]]);
                     /* @var $usageItemEntity UsageItemEntity */
                     if ($usageItemEntity === null) {
                         $usageItemEntity = new UsageItemEntity();
                         $usageItemEntity->usageType = $usageTypeEntity->id;
                         $usageItemEntity->name = $item;
                         $usageItemEntity->save();
                     }
                     //Hourly usage
                     $rec = new UsageHourlyEntity();
                     $rec->usageId = \Scalr::GenerateUID();
                     $rec->accountId = $fro->farm->farm->ClientID;
                     $rec->ccId = $po->cc->cc->ccId;
                     $rec->projectId = $po->project->projectId;
                     $rec->cloudLocation = $fro->farmRole->CloudLocation;
                     $rec->dtime = new DateTime($timestamp, $tzUtc);
                     $rec->envId = $fo->farm->EnvID;
                     $rec->farmId = $fo->farm->ID;
                     $rec->farmRoleId = $fro->farmRole->ID;
                     $rec->usageItem = $usageItemEntity->id;
                     $rec->platform = $fro->farmRole->Platform;
                     $rec->url = $fo->env->getUrl($fro->farmRole->Platform);
                     $rec->os = PriceEntity::OS_LINUX;
                     $rec->num = $countInstances;
                     $rec->cost = $cost * $countInstances;
                     $rec->save();
                     $logger->log(static::PAST_HOURS_INIT > 0 ? 'DEBUG' : 'INFO', "-- role:'%s':%d platform:%s, min:%d - max:%d, cloudLocation:'%s', usageItem:'%s', " . "cost:%0.4f * %d = %0.3f", $fro->farmRole->Alias, $fro->farmRole->ID, $fro->farmRole->Platform, $fro->min, $fro->max, $fro->farmRole->CloudLocation, $usageItemEntity->id, $cost, $countInstances, $rec->cost);
                     //Update Daily table
                     $cadb->Execute("\n                                INSERT usage_d\n                                SET date = ?,\n                                    platform = ?,\n                                    cc_id = UNHEX(?),\n                                    project_id = UNHEX(?),\n                                    farm_id = ?,\n                                    env_id = ?,\n                                    cost = ?\n                                ON DUPLICATE KEY UPDATE cost = cost + ?\n                            ", [$rec->dtime->format('Y-m-d'), $rec->platform, $rec->ccId ? str_replace('-', '', $rec->ccId) : '00000000-0000-0000-0000-000000000000', $rec->projectId ? str_replace('-', '', $rec->projectId) : '00000000-0000-0000-0000-000000000000', $rec->farmId ? $rec->farmId : 0, $rec->envId ? $rec->envId : 0, $rec->cost, $rec->cost]);
                     //Updates Quarterly Budget
                     if ($rec->ccId) {
                         $cadb->Execute("\n                                    INSERT quarterly_budget\n                                    SET year = ?,\n                                        subject_type = ?,\n                                        subject_id = UNHEX(?),\n                                        quarter = ?,\n                                        budget = 1000,\n                                        cumulativespend = ?\n                                    ON DUPLICATE KEY UPDATE cumulativespend = cumulativespend + ?\n                                ", [$period->year, QuarterlyBudgetEntity::SUBJECT_TYPE_CC, str_replace('-', '', $rec->ccId), $period->quarter, $rec->cost, $rec->cost]);
                     }
                     if ($rec->projectId) {
                         $cadb->Execute("\n                                    INSERT quarterly_budget\n                                    SET year = ?,\n                                        subject_type = ?,\n                                        subject_id = UNHEX(?),\n                                        quarter = ?,\n                                        budget = 1000,\n                                        cumulativespend = ?\n                                    ON DUPLICATE KEY UPDATE cumulativespend = cumulativespend + ?\n                                ", [$period->year, QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT, str_replace('-', '', $rec->projectId), $period->quarter, $rec->cost, $rec->cost]);
                     }
                 }
                 unset($fo);
             }
         }
         $dt->modify('-1 hour');
     } while ($pastIterations-- > 0);
     $dt = clone $start;
     $start->modify(sprintf("-%d hour", static::PAST_HOURS_INIT));
     $start->setTime(0, 0, 0);
     $date = $dt->format('Y-m-d');
     $hours = (int) $dt->format('H');
     do {
         $cadb->Execute("\n                    INSERT INTO `farm_usage_d` (\n                        `account_id`,\n                        `farm_role_id`,\n                        `usage_item`,\n                        `cc_id`,\n                        `project_id`,\n                        `date`,\n                        `platform`,\n                        `cloud_location`,\n                        `env_id`,\n                        `farm_id`,\n                        `role_id`,\n                        `cost`,\n                        `min_usage`,\n                        `max_usage`,\n                        `usage_hours`,\n                        `working_hours`)\n                    SELECT\n                        `account_id`,\n                        IFNULL(`farm_role_id`, 0) `farm_role_id`,\n                        `usage_item`,\n                        IFNULL(`cc_id`, '') `cc_id`,\n                        IFNULL(`project_id`, '') `project_id`,\n                        ? `date`,\n                        `platform`,\n                        `cloud_location`,\n                        IFNULL(`env_id`, 0) `env_id`,\n                        IFNULL(`farm_id`, 0) `farm_id`,\n                        IFNULL(`role_id`, 0) `role_id`,\n                        SUM(`cost`) `cost`,\n                        (CASE WHEN COUNT(`dtime`) >= ? THEN MIN(`num`) ELSE 0 END) `min_usage`,\n                        MAX(`num`) `max_usage`,\n                        SUM(`num`) `usage_hours`,\n                        COUNT(`dtime`) `working_hours`\n                    FROM `usage_h` `uh`\n                    WHERE `uh`.`dtime` BETWEEN ? AND ?\n                    AND `uh`.`farm_id` > 0\n                    AND `uh`.`farm_role_id` > 0\n                    GROUP BY `uh`.`account_id` , `uh`.`farm_role_id` , `uh`.`usage_item`\n                    ON DUPLICATE KEY UPDATE\n                        `cost` = VALUES(`cost`),\n                        `min_usage` = VALUES(`min_usage`),\n                        `max_usage` = VALUES(`max_usage`),\n                        `usage_hours` = VALUES(`usage_hours`),\n                        `working_hours` = VALUES(`working_hours`)\n                ", ["{$date} 00:00:00", $hours, "{$date} 00:00:00", "{$date} 23:00:00"]);
         $dt->modify('-1 day');
         $date = $dt->format('Y-m-d');
         $hours = 24;
     } while ($dt >= $start);
     $logger->info("Finished AnalyticsDemo process");
     $logger->info("Memory usage: %0.3f Mb", memory_get_usage() / 1024 / 1024);
     return new ArrayObject();
 }
Пример #22
0
 /**
  * xSavePriceAction
  *
  * @param  string   $platform              The cloud platform
  * @param  string   $cloudLocation         The cloud location
  * @param  JsonData $prices                Price list
  * @param  string   $url                   optional The url of the cloud
  * @param  string   $effectiveDate         optional The date when the prices will be applied
  * @param  boolean  $forbidAutomaticUpdate optional
  */
 public function xSavePriceAction($platform, $cloudLocation, JsonData $prices, $url = '', $effectiveDate = null, $forbidAutomaticUpdate = false)
 {
     list($curdate, $effectiveDate) = $this->handleEffectiveDate($effectiveDate);
     $service = $this->getContainer()->analytics->prices;
     $priceHistory = new PriceHistoryEntity();
     $priceHistory->platform = $platform;
     $priceHistory->cloudLocation = $cloudLocation;
     $priceHistory->url = $service->normalizeUrl($url);
     $priceHistory->accountId = $this->user->getAccountId();
     $priceHistory->applied = $effectiveDate;
     $this->getContainer()->analytics->prices->get($priceHistory);
     if ($priceHistory->priceId !== null) {
         //We have found recent prices
         if ($effectiveDate->format('Y-m-d') != $priceHistory->applied->format('Y-m-d')) {
             //We should generate a new version of the price
             $priceHistory->priceId = null;
             $priceHistory->applied = $effectiveDate;
         }
     }
     $details = new ArrayCollection();
     $found = [];
     foreach ($prices as $price) {
         if ($price['type'] && $price['priceLinux']) {
             $item = new PriceEntity();
             $item->instanceType = $price['type'];
             $item->os = PriceEntity::OS_LINUX;
             $item->name = $price['name'];
             $item->cost = floatval($price['priceLinux']);
             $found[$item->instanceType . '-' . $item->os] = $item;
             $details->append($item);
         }
         if ($price['type'] && $price['priceWindows']) {
             $item = new PriceEntity();
             $item->instanceType = $price['type'];
             $item->os = PriceEntity::OS_WINDOWS;
             $item->name = $price['name'];
             $item->cost = floatval($price['priceWindows']);
             $found[$item->instanceType . '-' . $item->os] = $item;
             $details->append($item);
         }
     }
     //Gets actual pricing on date from databse
     $collection = $this->getContainer()->analytics->prices->getActualPrices($platform, $cloudLocation, $priceHistory->url, $priceHistory->applied);
     //Compares if some price has been changed.
     if (!$collection->count()) {
         //There aren't any prices yet. We need to save them.
         $bChanged = true;
     } else {
         $bChanged = false;
         //For each previous price compare difference with new one
         foreach ($collection as $priceEntity) {
             $key = $priceEntity->instanceType . '-' . $priceEntity->os;
             if (isset($found[$key])) {
                 if (abs($found[$key]->cost - $priceEntity->cost) > 9.0E-7) {
                     unset($found[$key]);
                     $bChanged = true;
                     break;
                 }
                 unset($found[$key]);
             } else {
                 $bChanged = true;
                 break;
             }
         }
         //Some new price for new instance type is added while other prices reman untouched
         if (!$bChanged && count($found)) {
             foreach ($found as $priceEntity) {
                 //Zerro prices should not be taken into account if they don't exist before
                 if ($priceEntity->cost >= 1.0E-6) {
                     $bChanged = true;
                     break;
                 }
             }
         }
     }
     if ($bChanged) {
         //Saving actually only when there is some change
         $priceHistory->setDetails($details);
         $service->save($priceHistory);
         $this->getContainer()->analytics->events->fireChangeCloudPricingEvent($platform, $url);
     }
     if ($priceHistory->platform == SERVER_PLATFORMS::EC2) {
         SettingEntity::setValue(SettingEntity::ID_FORBID_AUTOMATIC_UPDATE_AWS_PRICES, $forbidAutomaticUpdate ? '1' : '0');
     }
     $this->response->success('Prices have been updated');
 }
Пример #23
0
 public function defaultAction()
 {
     $this->response->page('ui/analytics/dashboard/view.js', ['quarters' => SettingEntity::getQuarters(true)], ['/ui/analytics/analytics.js'], ['/ui/analytics/analytics.css']);
 }
Пример #24
-1
 /**
  * Constructor
  *
  * @param   string       $start     The start date of the period 'YYYY-mm-dd'
  * @param   string       $end       optional End date
  * @param   string       $timezone  optional Timezone
  * @throws  \InvalidArgumentException
  */
 public function __construct($start, $end = null, $timezone = 'UTC')
 {
     $this->mode = 'quarter';
     $this->timezone = new DateTimeZone($timezone);
     $this->today = $this->getTodayDate();
     $this->start = new DateTime($start instanceof DateTime ? $start->format('Y-m-d 00:00:00') : $start, $this->timezone);
     $this->end = !empty($end) ? new DateTime($end instanceof DateTime ? $end->format('Y-m-d 00:00:00') : $end, $this->timezone) : null;
     $quarters = new Quarters(SettingEntity::getQuarters());
     $this->interval = '1 week';
     $currentPeriod = $quarters->getPeriodForDate($this->start);
     $this->start = $currentPeriod->start;
     $this->end = $currentPeriod->end;
     $this->wholePeriodPerviousEnd = clone $this->start;
     $this->wholePeriodPerviousEnd->modify('-1 day');
     $prevPeriod = $quarters->getPeriodForDate($this->wholePeriodPerviousEnd);
     $this->prevStart = $prevPeriod->start;
     $this->prevInterval = new \DateInterval('P' . sprintf("%d", $this->start->diff($this->end, true)->days) . 'D');
     $this->determinePrevEnd();
     $endoftheday = new \DateInterval('PT23H59M59S');
     $this->end->add($endoftheday);
     $this->prevEnd->add($endoftheday);
     $this->wholePeriodPerviousEnd->add($endoftheday);
     if (!$this->di) {
         $this->di = \DateInterval::createFromDateString($this->interval);
     }
     $this->dt = clone $this->start;
 }