Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

replace with notification #7

Open
github-actions bot opened this issue Jan 12, 2024 · 0 comments
Open

replace with notification #7

github-actions bot opened this issue Jan 12, 2024 · 0 comments
Labels

Comments

@github-actions
Copy link

/ ///////////////////////////////////////////////////////////////////////////////

/**

// TODO replace with notification

    }


    /// ///////////////////////////////////////////////////////////////////////////////
    /// DEVICES
    /// ///////////////////////////////////////////////////////////////////////////////

    /**
     * @param $d array device config
     * @return array
     */
    public function device_status(array $d): array {
        $d['uri'] = $d['path'];

        $missingKeys = array_diff_key(array_flip(["uuid", "adapter", "uri"]), $d);
        if (!empty($missingKeys)) {
            $s = implode(', ', array_keys($missingKeys));
            throw new Exception("Device status requested without {$s} defined.");
        }

        $msg = [];
        $s = [
            "health" => "unknown",
            "message" => []
        ];


        if (!$this->is_uuidv4($d["uuid"])) {
            $s["health"] = "degraded";
            $msg[] = "Device key `{$d["uuid"]}` is not a UUIDv4.";
        }

        if ( $d["adapter"] == "filesystem" and PHP_OS == "Linux") {
            if (is_dir($d["uri"] ?? "")) {
                $output = json_decode(shell_exec("findmnt -DJv --output fstype,source,target,fsroot,options,size,used,avail,use%,uuid,partuuid --target " . $d["path"]));
                $s["adapter"] = (array) $output->filesystems[0];
                if (is_writable($d["uri"])) {
                    $s["health"] = "online";
                    $msg[] = "ok";
                } else {
                    $s["health"] = "degraded";
                    $msg[] = "path `{$d["path"]}` is not writable";
                }
            } else {
                $d["health"] = "offline";
                $msg[] = "`{$d["path"]}` is not a directory or is missing";
            }
        }

        $s['message'] = implode('; ', $msg);
        if ($s["health"] != "online") { $this->notify->send("Storage device `{$d["uuid"]}` `{$s["health"]}`: {$s["message"]}", notify_admins: true); }
        return $s;
    }

    function findDeviceDgs($data, $uuid)
    {
        $res = [];
        foreach ($data as $item) {

            foreach (($item['devices'] ?? []) as $k => $device) {
                if ($device['uuid'] === $uuid) {
                    $subres = $item['devices'][$k];
                    $subres['uuid'] = $item['uuid'];
                    $res[] = $subres;

                    break; // Stop searching for this device once found
                }
            }
        }
        return $res;
    }


    public function devices($uuid = null, $log = true) {
        $res = $this->settings["stor"]["devices"];
        $dgs = $this->dgs();
        $key = "09e0ef57-86e6-4376-b466-a7d2af31474e";
        if (array_key_exists($key, $res)) { $res[$key]["path"] = $this->settings["glued"]["datapath"] . "/" . basename(__ROOT__)  . "/data"; }
        $key = "86159ff5-f513-4852-bb3a-7f4657a35301";
        if (array_key_exists($key, $res)) { $res[$key]["path"] = sys_get_temp_dir(); }
        if ($uuid) {
            if (array_key_exists($uuid,$res)) {
                $res = array_intersect_key($res, [$uuid => null]); // return $res containing the only key, $uuid
            }
            else return [];
        }
        foreach ($res as $k=>&$v) {
            $v["uuid"] = $k;
            $v["status"] = $this->device_status($v);
            $v["status"]["dgs"] = $this->findDeviceDgs($dgs, $k);
        }

        if ($log == 1) {
            $q1 = "INSERT INTO `t_stor_devices` (`uuid`, `data`) VALUES (uuid_to_bin(?, true), ?) ON DUPLICATE KEY UPDATE `data` = VALUES(`data`)";
            $q2 = "INSERT IGNORE INTO `t_stor_configlog` (`uuid`, `data`) VALUES (uuid_to_bin(?, true), ?) ON DUPLICATE KEY UPDATE `ts_logged` = CURRENT_TIMESTAMP(1)";
            $q3 = "INSERT INTO `t_stor_statuslog` (`uuid_dev`, `data`, `uuid_dg`, `role`, `prio`) VALUES (uuid_to_bin(?, true), ?, uuid_to_bin(?, true), ?, ?) ON DUPLICATE KEY UPDATE `ts_updated` = CURRENT_TIMESTAMP(1);";
            $s1 = $this->mysqli->prepare($q1);
            $s2 = $this->mysqli->prepare($q2);
            $s3 = $this->mysqli->prepare($q3);

            foreach ($res as $k => $vv) {
                $s = $vv['status'] ?? [];
                unset($vv['status']);
                $vv = json_encode($vv ?? []);
                $this->mysqli->begin_transaction();
                $s1->bind_param("ss", $k, $vv);
                $s1->execute();
                $s2->bind_param("ss", $k, $vv);
                $s2->execute();
                foreach ($s["dgs"] as $dg) {
                    $ss = json_encode($s);
                    $s3->bind_param("ssssd", $k, $ss, $dg['uuid'],$dg['role'],$dg['prio']);
                    $s3->execute();
                }
                $this->mysqli->commit();
            }
        }
        $res = array_values($res);
        if (!is_null($uuid)) { return $res[0]; }
        return $res;
    }


    public function devices_r1(Request $request, Response $response, array $args = []): Response {
        $res = $this->devices($args['id'] ?? null);
        if ($res == []) { return $response->withJson(['message' => "Not found."])->withStatus(404); }
        return $response->withJson(['data' => $res]);

    }

    /// ///////////////////////////////////////////////////////////////////////////////
    /// DGS
    /// ///////////////////////////////////////////////////////////////////////////////


    function get_dg_uuid($uuid) {
        //if ($uuid == 'default') { $uuid = $this->get_default_dg(); }
        if (array_key_exists($uuid, $this->settings['stor']['dgs'])) { return $uuid; }
        throw new Exception("Dg `{$uuid}` used as device parent but not configured.", 500);
        // TODO replace with notification
    }

    public function dgs($uuid = null, $log = true)
    {
        $dgs = $this->settings['stor']['dgs'] ?? [];
        foreach ($dgs as $key => &$item) {
            $item['uuid'] = $key;
            if (!array_key_exists('devices', $item)) { $item['devices'] = []; }

            // Process each "device" and validate "uuid"
            foreach ($item['devices'] as $k => &$device) {
                    if (isset($device['uuid'])) {
                        $device['prio'] = isset($device['prio']) ? $device['prio'] : 1000;
                        if (!$this->is_uuidv4($device['uuid'])) {
                            // Handle case where "uuid" is not set or is not a valid UUIDv4 (remove entry)
                            $item['status']['message'][] = "Device UUID {$device['uuid']} is not a valid UUID string.";
                            unset($item['devices'][$k]);
                            if (!array_key_exists($device['uuid'], $this->settings['stor']['devices'])) {
                                $item['status']['message'][] = "Device {$device['uuid']} undefined.";
                                unset($item['devices'][$k]);
                            }
                        }
                    } else {
                        $item['status']['message'][] = "Dg contains a device item without the mandatory uuid.";
                    }
            }

            // Remove any null values caused by invalid devices
            $item['devices'] = array_values(array_filter($item['devices']));
            $item['status']['members'] = count($item['devices']);

            // Sort devices by "prio"
            if ($item['status']['members'] > 0) {
                usort($item['devices'], function ($a, $b) {
                    return $a['prio'] - $b['prio'];
                });
            } else {
                $item['status']['message'][] = "Dg doesn't have any devices configured.";
            }
        }



        if ($log == true) {
            $q1 = "INSERT INTO `t_stor_dgs` (`uuid`, `data`) VALUES (uuid_to_bin(?, true), ?) ON DUPLICATE KEY UPDATE `data` = VALUES(`data`)";
            $q2 = "INSERT IGNORE INTO `t_stor_configlog` (`uuid`, `data`) VALUES (uuid_to_bin(?, true), ?)";
            foreach ($dgs as $k => &$v) {
                $vv = $v;
                unset($vv['status']);
                $data = [$k, json_encode($vv ?? [])];
                $this->mysqli->begin_transaction();
                $this->mysqli->execute_query($q1, $data);
                if ($this->mysqli->affected_rows > 0) { $v['log'] = "New dg `{$k}` configured."; }
                $this->mysqli->execute_query($q2, $data);
                if ($this->mysqli->affected_rows > 0) { $v['log'] = "Dg `{$k}` configuration updated."; }
                $this->mysqli->commit();
            }
        }

        $res = array_values($dgs);
        if (!is_null($uuid)) { return $res[0]; }
        return $res;
    }

    public function dgs_r1(Request $request, Response $response, array $args = []): Response {
        $res = $this->dgs($args['id'] ?? null);
        if ($res == []) { return $response->withJson(['message' => "Not found."])->withStatus(404); }
        return $response->withJson(['data' => $res]);
    }

    /// ///////////////////////////////////////////////////////////////////////////////
    /// BUCKETS
    /// ///////////////////////////////////////////////////////////////////////////////


    public function get_buckets($bucket = null): array
    {
        $where = '';
        if (!is_null($bucket)) {
            $where = 'WHERE bucket = uuid_to_bin(?, true)';
            $arg = [ $bucket ];
        }

        $q = "
        SELECT 
          bin_to_uuid(b.`uuid`, 1) AS `bucket`,
          b.name AS `name`,
          bin_to_uuid(bd.`dg`, 1) AS `dg`,
          s.dg_health,
          s.dev_uuid,
          s.dev_health,
          s.dev_prio,
          s.dev_role,
          s.dev_adapter,
          s.status_ts as ts
        FROM `t_stor_buckets` b
        LEFT JOIN t_stor_bucket_dgs bd ON bd.bucket = b.uuid
        LEFT JOIN (
          SELECT dg_uuid, dg_health, dev_uuid, dev_health, status_ts, dev_prio, dev_role, dev_adapter
          FROM v_stor_status
        ) s ON s.dg_uuid = bin_to_uuid(bd.dg, 1)
        {$where}
        ORDER BY 
          bucket,
          CASE WHEN s.dev_health = 'online' THEN 0 ELSE 1 END,
          dev_prio
        ";

        $res = $this->mysqli->execute_query($q, $arg ?? []);
        $data = $res->fetch_all(MYSQLI_ASSOC);
        $unflat = [];


        foreach ($data as $row) {
            // Check if the bucket already exists in the unflattened result
            if (!isset($unflat[$row['bucket']])) {
                // If not, create a new entry for the bucket
                $unflat[$row['bucket']] = [
                    'uuid' => $row['bucket'],
                    'name' => $row['name'],
                    'ts' => $row['ts'],
                    'dgs' => [],
                    'devices' => [],
                ];
            }

            // Check if the dg already exists in the dgs array
            $dgExists = false;
            foreach ($unflat[$row['bucket']]['dgs'] as &$dgArray) {
                if ($dgArray['uuid'] === $row['dg']) {
                    $dgExists = true;
                    break;
                }
            }
            // If the dg does not exist, create a new entry for dg
            if (!$dgExists) {
                $unflat[$row['bucket']]['dgs'][] = [
                    'uuid' => $row['dg'],
                    'health' => $row['dg_health'],
                ];
            }

            // Check if the dg already exists in the dgs array
            $devExists = false;
            foreach ($unflat[$row['bucket']]['devices'] as &$devArray) {
                if ($devArray['uuid'] === $row['dev_uuid']) {
                    $devExists = true;
                    break;
                }
            }

            // If the dg does not exist, create a new entry for dg
            if (!$devExists) {
                $unflat[$row['bucket']]['devices'][] = [
                    'uuid' => $row['dev_uuid'],
                    'health' => $row['dev_health'],
                    'role' => $row['dev_role'],
                    'prio' => $row['dev_prio'],
                    'adapter' => $row['dev_adapter'],
                ];
            }
        }
        $res = array_values($unflat);
        if (!is_null($bucket)) {
            $res = $res[0];
        }
        return $res;
    }
    public function buckets_r1(Request $request, Response $response, array $args = []): Response {
        $params = $request->getQueryParams();
        $unflat = $this->get_buckets($args['id'] ?? null);
        $data = [
            'timestamp' => microtime(),
            'status' => 'Buckets r1 OK',
            'data' => $unflat,
            'service' => basename(__ROOT__),
        ];
        return $response->withJson($data);
    }
@github-actions github-actions bot added the todo label Jan 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

0 participants