diff --git a/.gitignore b/.gitignore index cae5d6494..30be2fe49 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ bin */**/CONTRIBUTING.md */**/SECURITY.md */**/RELEASE.md +*/**/AGENTS.md # ignore codestyle tools config */*/psalm.xml */*/psalm.xml.license diff --git a/composer.json b/composer.json index c4bfbdb6a..0a8cc9cbb 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "pear/archive_tar": "^1.6.0", "pear/pear-core-minimal": "^1.10", "php-http/guzzle7-adapter": "^1.1.0", - "php-opencloud/openstack": "^3.14", + "php-opencloud/openstack": "^3.17", "phpseclib/phpseclib": "^3.0.55", "pimple/pimple": "^3.6.2", "predis/predis": "^3.4.2", diff --git a/composer.lock b/composer.lock index 4f4f33e78..ea3dffc62 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "60ac218433a3dcf5a4a46a452bba3674", + "content-hash": "6396cd688c19613ee774ae3b13efa1e3", "packages": [ { "name": "aws/aws-crt-php", @@ -2858,21 +2858,21 @@ }, { "name": "php-opencloud/openstack", - "version": "v3.14.0", + "version": "v3.17.0", "source": { "type": "git", "url": "https://github.com/php-opencloud/openstack.git", - "reference": "b92ea5581ca91779b88f08b1b44e6ca880b34fc3" + "reference": "8b0aa946dc89f942c69d2968caedaa4d58201aa7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-opencloud/openstack/zipball/b92ea5581ca91779b88f08b1b44e6ca880b34fc3", - "reference": "b92ea5581ca91779b88f08b1b44e6ca880b34fc3", + "url": "https://api.github.com/repos/php-opencloud/openstack/zipball/8b0aa946dc89f942c69d2968caedaa4d58201aa7", + "reference": "8b0aa946dc89f942c69d2968caedaa4d58201aa7", "shasum": "" }, "require": { - "guzzlehttp/guzzle": "^7.0", - "guzzlehttp/psr7": ">=1.7", + "guzzlehttp/guzzle": "^7.5.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", "guzzlehttp/uri-template": "^0.2 || ^1.0", "justinrainbow/json-schema": "^5.2 || ^6.0", "php": "^7.2.5 || ^8.0" @@ -2929,9 +2929,9 @@ ], "support": { "issues": "https://github.com/php-opencloud/openstack/issues", - "source": "https://github.com/php-opencloud/openstack/tree/v3.14.0" + "source": "https://github.com/php-opencloud/openstack/tree/v3.17.0" }, - "time": "2025-05-30T09:26:42+00:00" + "time": "2026-06-01T14:33:34+00:00" }, { "name": "phpseclib/phpseclib", diff --git a/composer/autoload_classmap.php b/composer/autoload_classmap.php index e5b61b1f4..2ad39cf8a 100644 --- a/composer/autoload_classmap.php +++ b/composer/autoload_classmap.php @@ -1582,6 +1582,7 @@ 'OpenStack\\Compute\\v2\\Models\\Limit' => $vendorDir . '/php-opencloud/openstack/src/Compute/v2/Models/Limit.php', 'OpenStack\\Compute\\v2\\Models\\QuotaSet' => $vendorDir . '/php-opencloud/openstack/src/Compute/v2/Models/QuotaSet.php', 'OpenStack\\Compute\\v2\\Models\\Server' => $vendorDir . '/php-opencloud/openstack/src/Compute/v2/Models/Server.php', + 'OpenStack\\Compute\\v2\\Models\\ServerGroup' => $vendorDir . '/php-opencloud/openstack/src/Compute/v2/Models/ServerGroup.php', 'OpenStack\\Compute\\v2\\Params' => $vendorDir . '/php-opencloud/openstack/src/Compute/v2/Params.php', 'OpenStack\\Compute\\v2\\Service' => $vendorDir . '/php-opencloud/openstack/src/Compute/v2/Service.php', 'OpenStack\\Identity\\v2\\Api' => $vendorDir . '/php-opencloud/openstack/src/Identity/v2/Api.php', diff --git a/composer/autoload_static.php b/composer/autoload_static.php index 91f87b82b..3cbf38512 100644 --- a/composer/autoload_static.php +++ b/composer/autoload_static.php @@ -2137,6 +2137,7 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'OpenStack\\Compute\\v2\\Models\\Limit' => __DIR__ . '/..' . '/php-opencloud/openstack/src/Compute/v2/Models/Limit.php', 'OpenStack\\Compute\\v2\\Models\\QuotaSet' => __DIR__ . '/..' . '/php-opencloud/openstack/src/Compute/v2/Models/QuotaSet.php', 'OpenStack\\Compute\\v2\\Models\\Server' => __DIR__ . '/..' . '/php-opencloud/openstack/src/Compute/v2/Models/Server.php', + 'OpenStack\\Compute\\v2\\Models\\ServerGroup' => __DIR__ . '/..' . '/php-opencloud/openstack/src/Compute/v2/Models/ServerGroup.php', 'OpenStack\\Compute\\v2\\Params' => __DIR__ . '/..' . '/php-opencloud/openstack/src/Compute/v2/Params.php', 'OpenStack\\Compute\\v2\\Service' => __DIR__ . '/..' . '/php-opencloud/openstack/src/Compute/v2/Service.php', 'OpenStack\\Identity\\v2\\Api' => __DIR__ . '/..' . '/php-opencloud/openstack/src/Identity/v2/Api.php', diff --git a/composer/installed.json b/composer/installed.json index 362c1a10e..e50d2bfd3 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -2981,22 +2981,22 @@ }, { "name": "php-opencloud/openstack", - "version": "v3.14.0", - "version_normalized": "3.14.0.0", + "version": "v3.17.0", + "version_normalized": "3.17.0.0", "source": { "type": "git", "url": "https://github.com/php-opencloud/openstack.git", - "reference": "b92ea5581ca91779b88f08b1b44e6ca880b34fc3" + "reference": "8b0aa946dc89f942c69d2968caedaa4d58201aa7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-opencloud/openstack/zipball/b92ea5581ca91779b88f08b1b44e6ca880b34fc3", - "reference": "b92ea5581ca91779b88f08b1b44e6ca880b34fc3", + "url": "https://api.github.com/repos/php-opencloud/openstack/zipball/8b0aa946dc89f942c69d2968caedaa4d58201aa7", + "reference": "8b0aa946dc89f942c69d2968caedaa4d58201aa7", "shasum": "" }, "require": { - "guzzlehttp/guzzle": "^7.0", - "guzzlehttp/psr7": ">=1.7", + "guzzlehttp/guzzle": "^7.5.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", "guzzlehttp/uri-template": "^0.2 || ^1.0", "justinrainbow/json-schema": "^5.2 || ^6.0", "php": "^7.2.5 || ^8.0" @@ -3011,7 +3011,7 @@ "phpunit/phpunit": ">=8.5.23 <9.0", "psr/log": "^1.0" }, - "time": "2025-05-30T09:26:42+00:00", + "time": "2026-06-01T14:33:34+00:00", "type": "library", "extra": { "branch-alias": { @@ -3055,7 +3055,7 @@ ], "support": { "issues": "https://github.com/php-opencloud/openstack/issues", - "source": "https://github.com/php-opencloud/openstack/tree/v3.14.0" + "source": "https://github.com/php-opencloud/openstack/tree/v3.17.0" }, "install-path": "../php-opencloud/openstack" }, diff --git a/composer/installed.php b/composer/installed.php index 6d9d1f222..18688f88a 100644 --- a/composer/installed.php +++ b/composer/installed.php @@ -425,9 +425,9 @@ 'dev_requirement' => false, ), 'php-opencloud/openstack' => array( - 'pretty_version' => 'v3.14.0', - 'version' => '3.14.0.0', - 'reference' => 'b92ea5581ca91779b88f08b1b44e6ca880b34fc3', + 'pretty_version' => 'v3.17.0', + 'version' => '3.17.0.0', + 'reference' => '8b0aa946dc89f942c69d2968caedaa4d58201aa7', 'type' => 'library', 'install_path' => __DIR__ . '/../php-opencloud/openstack', 'aliases' => array(), diff --git a/php-opencloud/openstack/src/Compute/v2/Api.php b/php-opencloud/openstack/src/Compute/v2/Api.php index ee4adfa00..571543a73 100644 --- a/php-opencloud/openstack/src/Compute/v2/Api.php +++ b/php-opencloud/openstack/src/Compute/v2/Api.php @@ -18,6 +18,11 @@ public function __construct() $this->params = new Params(); } + private function serverParam(array $param): array + { + return array_merge($param, ['path' => 'server']); + } + public function getLimits(): array { return [ @@ -189,21 +194,21 @@ public function deleteImageMetadataKey(): array public function postServer(): array { return [ - 'path' => 'servers', - 'method' => 'POST', - 'jsonKey' => 'server', - 'params' => [ - 'imageId' => $this->notRequired($this->params->imageId()), - 'flavorId' => $this->params->flavorId(), - 'personality' => $this->params->personality(), - 'metadata' => $this->notRequired($this->params->metadata()), - 'name' => $this->isRequired($this->params->name('server')), - 'securityGroups' => $this->params->securityGroups(), - 'userData' => $this->params->userData(), - 'availabilityZone' => $this->params->availabilityZone(), - 'networks' => $this->params->networks(), - 'blockDeviceMapping' => $this->params->blockDeviceMapping(), - 'keyName' => $this->params->keyName(), + 'path' => 'servers', + 'method' => 'POST', + 'params' => [ + 'imageId' => $this->serverParam($this->notRequired($this->params->imageId())), + 'flavorId' => $this->serverParam($this->params->flavorId()), + 'personality' => $this->serverParam($this->params->personality()), + 'metadata' => $this->serverParam($this->notRequired($this->params->metadata())), + 'name' => $this->serverParam($this->isRequired($this->params->name('server'))), + 'securityGroups' => $this->serverParam($this->params->securityGroups()), + 'userData' => $this->serverParam($this->params->userData()), + 'availabilityZone' => $this->serverParam($this->params->availabilityZone()), + 'networks' => $this->serverParam($this->params->networks()), + 'blockDeviceMapping' => $this->serverParam($this->params->blockDeviceMapping()), + 'keyName' => $this->serverParam($this->params->keyName()), + 'schedulerHints' => $this->params->schedulerHints(), ], ]; } @@ -708,6 +713,68 @@ public function deleteKeypair(): array ]; } + public function getServerGroups(): array + { + return [ + 'method' => 'GET', + 'path' => 'os-server-groups', + 'params' => [ + 'allProjects' => $this->params->allProjects(), + 'limit' => $this->params->limit(), + 'offset' => $this->params->offset(), + ], + ]; + } + + public function getServerGroup(): array + { + return [ + 'method' => 'GET', + 'path' => 'os-server-groups/{id}', + 'params' => [ + 'id' => $this->params->urlId('server group'), + ], + ]; + } + + public function postServerGroup(): array + { + return [ + 'method' => 'POST', + 'path' => 'os-server-groups', + 'jsonKey' => 'server_group', + 'params' => [ + 'name' => $this->isRequired($this->params->name('server group')), + 'policies' => $this->isRequired($this->params->serverGroupPolicies()), + ], + ]; + } + + public function postServerGroupWithPolicy(): array + { + return [ + 'method' => 'POST', + 'path' => 'os-server-groups', + 'jsonKey' => 'server_group', + 'params' => [ + 'name' => $this->isRequired($this->params->name('server group')), + 'policy' => $this->isRequired($this->params->serverGroupPolicy()), + 'rules' => $this->notRequired($this->params->serverGroupRules()), + ], + ]; + } + + public function deleteServerGroup(): array + { + return [ + 'method' => 'DELETE', + 'path' => 'os-server-groups/{id}', + 'params' => [ + 'id' => $this->params->urlId('server group'), + ], + ]; + } + public function postSecurityGroup(): array { return [ diff --git a/php-opencloud/openstack/src/Compute/v2/Models/ServerGroup.php b/php-opencloud/openstack/src/Compute/v2/Models/ServerGroup.php new file mode 100644 index 000000000..a36aba833 --- /dev/null +++ b/php-opencloud/openstack/src/Compute/v2/Models/ServerGroup.php @@ -0,0 +1,111 @@ + 'projectId', + 'user_id' => 'userId', + ]; + + protected $resourceKey = 'server_group'; + protected $resourcesKey = 'server_groups'; + + public function retrieve() + { + $response = $this->execute($this->api->getServerGroup(), ['id' => (string) $this->id]); + $this->populateFromResponse($response); + } + + public function create(array $userOptions): Creatable + { + if (isset($userOptions['policy'], $userOptions['policies'])) { + throw new \RuntimeException('Provide either "policy" or "policies", not both.'); + } + + if (isset($userOptions['rules']) && !isset($userOptions['policy'])) { + throw new \RuntimeException('"rules" requires "policy".'); + } + + if (!isset($userOptions['policy']) && !isset($userOptions['policies'])) { + throw new \RuntimeException('"policy" or "policies" must be set.'); + } + + $definition = isset($userOptions['policy']) + ? $this->api->postServerGroupWithPolicy() + : $this->api->postServerGroup(); + + $response = $this->execute($definition, $userOptions); + + return $this->populateFromResponse($response); + } + + public function delete() + { + $this->execute($this->api->deleteServerGroup(), ['id' => (string) $this->id]); + } + + public function populateFromArray(array $array): self + { + $array = Utils::flattenJson($array, $this->resourceKey); + + if (isset($array['policy']) && !isset($array['policies'])) { + $array['policies'] = [$array['policy']]; + } + + if (isset($array['policies']) && is_array($array['policies']) && !isset($array['policy']) && count($array['policies']) > 0) { + $array['policy'] = reset($array['policies']); + } + + $array += [ + 'members' => [], + 'metadata' => [], + 'policies' => [], + 'rules' => [], + ]; + + return parent::populateFromArray($array); + } +} diff --git a/php-opencloud/openstack/src/Compute/v2/Params.php b/php-opencloud/openstack/src/Compute/v2/Params.php index b8a8b73ee..1297b6b1e 100644 --- a/php-opencloud/openstack/src/Compute/v2/Params.php +++ b/php-opencloud/openstack/src/Compute/v2/Params.php @@ -366,6 +366,16 @@ public function blockDeviceMapping(): array ]; } + public function schedulerHints(): array + { + return [ + 'type' => self::OBJECT_TYPE, + 'location' => self::JSON, + 'sentAs' => 'os:scheduler_hints', + 'description' => 'Scheduler hints to pass alongside the server create request, for example ["group" => "{serverGroupId}"].', + ]; + } + public function filterHost(): array { return [ @@ -499,6 +509,55 @@ public function keypairType(): array ]; } + public function allProjects(): array + { + return [ + 'type' => self::BOOL_TYPE, + 'location' => self::QUERY, + 'sentAs' => 'all_projects', + 'description' => '(Admin only) Set this to true to list server groups from all projects.', + ]; + } + + public function offset(): array + { + return [ + 'type' => self::INT_TYPE, + 'location' => self::QUERY, + 'description' => 'The offset of the first server group to return.', + ]; + } + + public function serverGroupPolicies(): array + { + return [ + 'type' => self::ARRAY_TYPE, + 'location' => self::JSON, + 'description' => 'The list of policies for the server group. In the base Compute API, a single policy must be provided in this array.', + 'items' => [ + 'type' => self::STRING_TYPE, + ], + ]; + } + + public function serverGroupPolicy(): array + { + return [ + 'type' => self::STRING_TYPE, + 'location' => self::JSON, + 'description' => 'The server group policy name. This is available with Compute microversion 2.64 or later.', + ]; + } + + public function serverGroupRules(): array + { + return [ + 'type' => self::OBJECT_TYPE, + 'location' => self::JSON, + 'description' => 'The server group rules object. This is available with Compute microversion 2.64 or later.', + ]; + } + public function flavorRam(): array { return [ diff --git a/php-opencloud/openstack/src/Compute/v2/Service.php b/php-opencloud/openstack/src/Compute/v2/Service.php index 968b22ebc..be701b5be 100644 --- a/php-opencloud/openstack/src/Compute/v2/Service.php +++ b/php-opencloud/openstack/src/Compute/v2/Service.php @@ -15,6 +15,7 @@ use OpenStack\Compute\v2\Models\Limit; use OpenStack\Compute\v2\Models\QuotaSet; use OpenStack\Compute\v2\Models\Server; +use OpenStack\Compute\v2\Models\ServerGroup; /** * Compute v2 service for OpenStack. @@ -172,6 +173,45 @@ public function getKeypair(array $options = []): Keypair return $keypair; } + /** + * List server groups. + * + * @param array $options {@see \OpenStack\Compute\v2\Api::getServerGroups} + * @param callable|null $mapFn a callable function that will be invoked on every iteration of the list + * + * @return \Generator + */ + public function listServerGroups(array $options = [], ?callable $mapFn = null): \Generator + { + return $this->model(ServerGroup::class)->enumerate($this->api->getServerGroups(), $options, $mapFn); + } + + /** + * Create server group. + * + * @param array $options {@see \OpenStack\Compute\v2\Models\ServerGroup::create} + */ + public function createServerGroup(array $options): ServerGroup + { + return $this->model(ServerGroup::class)->create($options); + } + + /** + * Retrieve a server group object without calling the remote API. Any values provided in the array will populate the + * empty object, allowing you greater control without the expense of network transactions. To call the remote API + * and have the response populate the object, call {@see ServerGroup::retrieve}. + * + * @param array $options An array of attributes that will be set on the {@see ServerGroup} object. The array keys need to + * correspond to the class public properties. + */ + public function getServerGroup(array $options = []): ServerGroup + { + $serverGroup = $this->model(ServerGroup::class); + $serverGroup->populateFromArray($options); + + return $serverGroup; + } + /** * Shows rate and absolute limits for the tenant. */ diff --git a/php-opencloud/openstack/src/Identity/v3/Api.php b/php-opencloud/openstack/src/Identity/v3/Api.php index 418684c91..09a49e171 100644 --- a/php-opencloud/openstack/src/Identity/v3/Api.php +++ b/php-opencloud/openstack/src/Identity/v3/Api.php @@ -692,9 +692,10 @@ public function headGroupUser(): array public function postCredentials(): array { return [ - 'method' => 'POST', - 'path' => 'credentials', - 'params' => [ + 'method' => 'POST', + 'path' => 'credentials', + 'jsonKey' => 'credential', + 'params' => [ 'blob' => $this->params->blob(), 'projectId' => $this->params->projectId(), 'type' => $this->params->type('credential'), @@ -724,9 +725,10 @@ public function getCredential(): array public function patchCredential(): array { return [ - 'method' => 'PATCH', - 'path' => 'credentials/{id}', - 'params' => ['id' => $this->params->idUrl('credential')] + $this->postCredentials()['params'], + 'method' => 'PATCH', + 'path' => 'credentials/{id}', + 'jsonKey' => 'credential', + 'params' => ['id' => $this->params->idUrl('credential')] + $this->postCredentials()['params'], ]; } diff --git a/php-opencloud/openstack/src/Identity/v3/Models/Credential.php b/php-opencloud/openstack/src/Identity/v3/Models/Credential.php index 294a3ec1f..325a66ce5 100644 --- a/php-opencloud/openstack/src/Identity/v3/Models/Credential.php +++ b/php-opencloud/openstack/src/Identity/v3/Models/Credential.php @@ -39,6 +39,9 @@ class Credential extends OperatorResource implements Creatable, Updateable, Retr 'user_id' => 'userId', ]; + protected $resourceKey = 'credential'; + protected $resourcesKey = 'credentials'; + public function create(array $userOptions): Creatable { $response = $this->execute($this->api->postCredentials(), $userOptions); diff --git a/php-opencloud/openstack/src/Images/v2/Models/Image.php b/php-opencloud/openstack/src/Images/v2/Models/Image.php index 9cff176b6..5ee322f60 100644 --- a/php-opencloud/openstack/src/Images/v2/Models/Image.php +++ b/php-opencloud/openstack/src/Images/v2/Models/Image.php @@ -8,6 +8,7 @@ use OpenStack\Common\Resource\Alias; use OpenStack\Common\Resource\Creatable; use OpenStack\Common\Resource\Deletable; +use OpenStack\Common\Resource\HasWaiterTrait; use OpenStack\Common\Resource\Listable; use OpenStack\Common\Resource\OperatorResource; use OpenStack\Common\Resource\Retrievable; @@ -20,6 +21,8 @@ */ class Image extends OperatorResource implements Creatable, Listable, Retrievable, Deletable { + use HasWaiterTrait; + /** @var string */ public $status;