BaseJob.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. <?php
  2. namespace ModulesGarden\ProxmoxAddon\App\Jobs\Vps;
  3. use Illuminate\Database\Capsule\Manager as DB;
  4. use MGProvision\Proxmox\v2\models\Node;
  5. use ModulesGarden\ProxmoxAddon\App\Models\RangeVm;
  6. use ModulesGarden\ProxmoxAddon\App\Models\TaskHistory;
  7. use ModulesGarden\ProxmoxAddon\App\Models\VmModel;
  8. use ModulesGarden\ProxmoxAddon\App\Models\Whmcs\ActivityLog;
  9. use ModulesGarden\ProxmoxAddon\App\Models\Whmcs\ToDoList;
  10. use ModulesGarden\ProxmoxAddon\App\Repositories\RangeVmRepository;
  11. use ModulesGarden\ProxmoxAddon\App\Services\ApiService;
  12. use ModulesGarden\ProxmoxAddon\App\Services\EmailService;
  13. use ModulesGarden\ProxmoxAddon\App\Services\Vps\AdditionalDiskService;
  14. use ModulesGarden\ProxmoxAddon\App\Services\Vps\AdditionalMountPointService;
  15. use ModulesGarden\ProxmoxAddon\App\Services\Vps\AgentService;
  16. use ModulesGarden\ProxmoxAddon\App\Services\Vps\ContainerService;
  17. use ModulesGarden\ProxmoxAddon\App\Services\Vps\HostingService;
  18. use ModulesGarden\ProxmoxAddon\Core\Models\Whmcs\Configuration;
  19. use ModulesGarden\ProxmoxAddon\Core\ModuleConstants;
  20. use ModulesGarden\ProxmoxAddon\Core\Queue\Job;
  21. use ModulesGarden\ProxmoxAddon\Core\UI\Traits\WhmcsParams;
  22. use ModulesGarden\ProxmoxAddon\Core\Traits\Smarty;
  23. use function ModulesGarden\ProxmoxAddon\Core\Helper\sl;
  24. class BaseJob extends Job
  25. {
  26. use ApiService;
  27. use WhmcsParams;
  28. use HostingService;
  29. use Smarty;
  30. protected $params = [];
  31. /**
  32. * @var EmailService
  33. */
  34. protected $emailService;
  35. /**
  36. * @var ContainerService
  37. */
  38. protected $containerService;
  39. /**
  40. * @var AgentService
  41. */
  42. protected $agentService;
  43. /**
  44. * @var AdditionalDiskService
  45. */
  46. protected $additionalDiskService;
  47. /**
  48. * @var AdditionalMountPointService
  49. */
  50. protected $additionalMountPointService;
  51. protected function initServices() {
  52. $this->emailService = new EmailService();
  53. $this->containerService = new ContainerService();
  54. $this->agentService = new AgentService();
  55. $this->additionalDiskService = new AdditionalDiskService();
  56. $this->additionalMountPointService = new AdditionalMountPointService();
  57. }
  58. public function initParams() {
  59. if (!$this->model->rel_id)
  60. {
  61. new \InvalidArgumentException(sprintf("Job model: #%s rel_id cannot be empty", $this->model->id));
  62. }
  63. if (!function_exists('ModuleBuildParams'))
  64. {
  65. require_once ModuleConstants::getFullPathWhmcs('includes') . DIRECTORY_SEPARATOR . "modulefunctions.php";
  66. }
  67. $this->params = \ModuleBuildParams($this->model->rel_id);
  68. sl("whmcsParams")->setParams($this->params);
  69. \ModulesGarden\Servers\ProxmoxVps\Core\Helper\sl("whmcsParams")->setParams($this->params);
  70. $this->setHostingId($this->params['serviceid']);
  71. return $this;
  72. }
  73. protected function sleep($seconds = 60) {
  74. $this->model->setWaiting();
  75. $this->model->setRetryAfter(date("Y-m-d H:i:s", strtotime("+{$seconds} seconds")));
  76. $this->model->increaseRetryCount();
  77. }
  78. protected function vmidExistInWhmcs($vmid) {
  79. //vps
  80. $cfv = 'tblcustomfieldsvalues';
  81. $cfn = 'tblcustomfields';
  82. $h = 'tblhosting';
  83. $query = DB::table($cfv)
  84. ->rightJoin($cfn, "{$cfn}.id", "=", "{$cfv}.fieldid")
  85. ->leftJoin($h, "{$h}.id", "=", "{$cfv}.relid")
  86. ->where("{$cfn}.fieldname", "like", "vmid%")
  87. ->where("{$cfv}.value", $vmid)
  88. ->whereIn("{$h}.domainstatus", ['Active', "Suspended"]);
  89. if ($query->count()) {
  90. return true;
  91. }
  92. //cloud
  93. try
  94. {
  95. $query = VmModel::where("vmid", $vmid);
  96. return $query->count() > 0;
  97. }
  98. catch (\Exception $ex)
  99. { // table does not exist
  100. }
  101. return false;
  102. }
  103. protected function findFreeVmid($vmid) {
  104. for ($i = $vmid; $i <= 1000000; $i++) {
  105. if ($this->vmidExistInWhmcs($i)) {
  106. continue;
  107. }
  108. try {
  109. $res = $this->api()->get("/cluster/nextid", ['vmid' => $i]);
  110. } catch (\Exception $ex) {
  111. continue;
  112. }
  113. if ($res == $i) {
  114. return $i;
  115. }
  116. }
  117. throw new \Exception("Unable to obtain vmid");
  118. }
  119. protected function isVmRange(){
  120. if (RangeVm::ofServerId($this->getWhmcsParamByKey("serverid"))->count()) {
  121. return true;
  122. }
  123. return Configuration::where("setting", "proxmoxVPSMinimumVMID")->count() > 0;
  124. }
  125. protected function nextVmid() {
  126. $data = $this->api()->get("/cluster/nextid", []);
  127. $vmid = (int)$data ? (int)$data : 100;
  128. $vmid = $this->findFreeVmid($vmid);
  129. if ($this->isVmRange()) {
  130. $rageVm = new RangeVmRepository($this->getWhmcsParamByKey('serverid'));
  131. if (!$rageVm->has() && !$rageVm->getMin()) {
  132. return $vmid;
  133. } else {
  134. if (!$rageVm->getMax() && $rageVm->getMin())
  135. {
  136. $from = (int)$rageVm->getMin();
  137. $to = (int)$rageVm->getMin() * 100;
  138. }
  139. else
  140. {
  141. $from = (int)$rageVm->getMin();
  142. $to = (int)$rageVm->getMax();
  143. }
  144. }
  145. for ($i = $from; $i <= $to; $i++)
  146. {
  147. try
  148. {
  149. if ($this->vmidExistInWhmcs($i))
  150. {
  151. continue;
  152. }
  153. $res = $this->api()->get("/cluster/nextid", ['vmid' => $i]);
  154. if ((int)$res == $i)
  155. {
  156. $vmID = $i;
  157. break;
  158. }
  159. }
  160. catch (\Exception $ex)
  161. {
  162. continue;
  163. }
  164. }
  165. if (!$vmID)
  166. {
  167. throw new \Exception("VM Ranges have been exited for this server. Please setup VM Ranges", 321);
  168. }
  169. }
  170. return $vmID ? $vmID : $vmid;
  171. }
  172. protected function createTaskHistory($taskId, $action)
  173. {
  174. $type = str_replace(["qemu", "lxc"], ["VM", "CT"], $this->vm()->getVirtualization());
  175. $task = new TaskHistory();
  176. $task->fill([
  177. 'hosting_id' => $this->getWhmcsParamByKey("serviceid"),
  178. 'upid' => $taskId,
  179. 'name' => sprintf("%s %s - %s", $type, $this->vm()->getVmid(), $action),
  180. 'vmid' => $this->vm()->getVmid(),
  181. 'node' => $this->vm()->getNode(),
  182. 'status' => 0
  183. ])->save();
  184. }
  185. protected function getModelData()
  186. {
  187. return unserialize($this->model->data);
  188. }
  189. protected function putModelDataAndSave(array $newData)
  190. {
  191. $data = $this->getModelData();
  192. $data += $newData;
  193. $this->setModelDataAndSave($data);
  194. return $this;
  195. }
  196. protected function setModelDataAndSave(array $data)
  197. {
  198. $this->model->data = serialize($data);
  199. $this->model->save();
  200. return $this;
  201. }
  202. /**
  203. * @return \MGProvision\Proxmox\v2\models\Task
  204. * @throws \MGProvision\Proxmox\v2\ProxmoxApiException
  205. */
  206. protected function getTask()
  207. {
  208. $taskId = $this->getModelData()['taskId'];
  209. //Init API service
  210. if($this->getModelData()['templateNode']){
  211. $node = new Node($this->getModelData()['templateNode']);
  212. }else{
  213. $node = new Node($this->getModelData()['node']);
  214. }
  215. $node->setApi($this->api());
  216. return $node->task($taskId);
  217. }
  218. protected function isTask()
  219. {
  220. return !is_null($this->getModelData()['taskId']);
  221. }
  222. protected function isDone()
  223. {
  224. $taskId = $this->getModelData()['taskId'];
  225. if (!$taskId || !$this->getModelData()['node'])
  226. {
  227. return false;
  228. }
  229. $task = $this->getTask();
  230. if ($task->getStatus() == "running")
  231. {
  232. $this->log->success(sprintf("Waiting to finish. Task Id %s Node: %s ", $task->getUpid(), $task->getNode()));
  233. return false;
  234. }
  235. //Failed
  236. if ($task->getExitstatus() && $task->getExitstatus() != "OK")
  237. {
  238. $this->log->error($task->getExitstatus());
  239. $this->failed($task->getExitstatus());
  240. $data = $this->getModelData();
  241. unset($data['taskId'], $data['node']);
  242. $this->setModelDataAndSave($data);
  243. return false;
  244. }
  245. return true;
  246. }
  247. protected function isTaskRunning()
  248. {
  249. return $this->isTask() && $this->getTask()->isRunning();
  250. }
  251. protected function isFailed()
  252. {
  253. $task = $this->getTask();
  254. return $task->getExitstatus() && $task->getExitstatus() != "OK";
  255. }
  256. protected function failed($error) {
  257. if ((int)$this->model->retry_count != 21) {
  258. return;
  259. }
  260. if (!preg_match("/Create/", $this->model->job) || preg_match("/Clone/", $this->model->job)) {
  261. return;
  262. }
  263. //create new entery on to do list
  264. if ($this->configuration()->isToDoList()) {
  265. $title = sprintf('Creation Failed - Service ID: %s', $this->getWhmcsParamByKey('serviceid'));
  266. if (ToDoList::ofTitle($title)->pending()->count()) {
  267. return;
  268. }
  269. $entity = new ToDoList();
  270. $entity->fill(
  271. [
  272. 'date' => date("Y-m-d H:i:s"),
  273. "duedate" => date("Y-m-d H:i:s"),
  274. 'title' => $title,
  275. "status" => "Pending",
  276. "description" => $error,
  277. "admin" => 0,
  278. ]
  279. );
  280. $entity->save();
  281. }
  282. //send admin message
  283. if ($this->configuration()->getServiceCreationFailedTemplateId())
  284. {
  285. //init email template
  286. $this->emailService->template($this->configuration()->getServiceCreationFailedTemplateId());
  287. //check if already send
  288. $description = printf('Email Sent to Admin (%s) - Service ID: %s', $this->emailService->getVars()['messagename'], $this->getWhmcsParamByKey('serviceid'));
  289. if (ActivityLog::ofDescription($description)->today()->count() > 0)
  290. {
  291. return;
  292. }
  293. //email vars
  294. global $customadminpath;
  295. $adminDir = $customadminpath ? $customadminpath : "admin";
  296. $adminAreaLink = $GLOBALS['CONFIG']['SystemURL'] . "/{$adminDir}";
  297. $hosting = $GLOBALS['CONFIG']['SystemURL'] . "/{$adminDir}/clientsservices.php?id=" . $this->getWhmcsParamByKey('serviceid');
  298. $emailVars = [
  299. "client_id" => $this->getWhmcsParamByKey('clientsdetails')['id'],
  300. "service_id" => $this->getWhmcsParamByKey('serviceid'),
  301. "service_product" => "<a href='{$hosting}/'>{$hosting}</a>",
  302. "service_domain" => $this->getWhmcsParamByKey('domain'),
  303. "error_msg" => $error,
  304. "whmcs_admin_link" => "<a href='{$adminAreaLink}/'>{$adminAreaLink}</a>",
  305. ];
  306. $this->emailService->vars($emailVars)->sendToAdmin();
  307. logActivity($description);
  308. }
  309. }
  310. /**
  311. * @return \ModulesGarden\ProxmoxAddon\App\Models\Job:
  312. */
  313. protected function getParentModel() {
  314. if (is_null($this->model->parent_id)) {
  315. throw new \InvalidArgumentException("The Parent Id is not valid");
  316. }
  317. if ($this->parent) {
  318. return $this->parent;
  319. }
  320. return $this->parent = \ModulesGarden\ProxmoxAddon\App\Models\Job::ofId($this->model->parent_id)->firstOrFail();
  321. }
  322. protected function getParentModelData() {
  323. return unserialize($this->getParentModel()->data);
  324. }
  325. }