本次实验环境为Ubuntu2404,OpenStack版本为C版,单节点Allinone
配置基础环境
使用阿里源pypi依赖库
mkdir ~/.pip/
vi ~/.pip/pip.conf
[global]
index-url=http://mirrors.cloud.aliyuncs.com/pypi/simple/
[install]
trusted-host=mirrors.cloud.aliyuncs.com
Ubuntu2404自带python3.12环境,安装python3-pip工具即可
apt install python3-pip -y
Ubuntu 和 Debian 系统将系统 Python 环境标记为“受外部管理的环境”,需要使用虚拟环境或其他隔离方法来安装 Python 包,不然会报错
error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to
install.
If you wish to install a non-Debian-packaged Python package,
create a virtual environment using python3 -m venv path/to/venv.
Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
sure you have python3-full installed.
If you wish to install a non-Debian packaged Python application,
it may be easiest to use pipx install xyz, which will manage a
virtual environment for you. Make sure you have pipx installed.
See /usr/share/doc/python3.12/README.venv for more information.
note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.
apt install python3-venv -y
创建虚拟环境
python3 -m venv myenv
调用Keystone-api用户接口
激活虚拟环境
source myenv/bin/activate
更新升级pip3
python3 -m pip install --upgrade pip
安装requests库
pip3 install requests
官网文档:可以对角色、用户、项目、域等所有的操作进行api调用,基本上只要客户端命令能实现的,都可以调用api进行操作。
编写用户管理py文件
以下为官网示例的用户管理api接口,包含了用户的增删改查等功能:每个接口可对应查看到具体的细节
示例代码如下:包含用户的增删改查
import requests
import json
import logging
# 配置日志记录
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(message)s')
logger = logging.getLogger(__name__)
# 获取认证Token
def get_auth_token(controller_ip, domain, user, password):
"""
获取 OpenStack 用户认证Token
:param controller_ip: 控制节点 IP
:param domain: 用户域
:param user: 用户名
:param password: 用户密码
:return: 认证头部信息
"""
try:
url = f"http://{controller_ip}:5000/v3/auth/tokens"
body = {
"auth": {
"identity": {"methods": ["password"], "password": {"user": {"domain": {"name": domain}, "name": user, "password": password}}},
"scope": {"project": {"domain": {"name": domain}, "name": user}}
}
}
response = requests.post(url, json=body, headers={"Content-Type": "application/json"})
token = response.headers['X-Subject-Token']
logger.debug(f"获取Token值: {token}")
return {"X-Auth-Token": token}
except Exception as e:
logger.error(f"获取Token失败: {e}")
exit(1)
# 用户管理类
class UserManager:
def __init__(self, headers, url):
self.headers = headers
self.url = url
def create_user(self, name, password, description):
"""创建用户"""
body = {"user": {"name": name, "password": password, "description": description}}
response = requests.post(self.url, json=body, headers=self.headers)
logger.debug(f"创建用户响应: {response.text}")
return response.text
def get_users(self):
"""查询所有用户"""
response = requests.get(self.url, headers=self.headers)
logger.debug(f"查询用户响应: {response.text}")
return response.text
def get_user_id(self, name):
"""根据用户名获取用户ID"""
users = json.loads(self.get_users())['users']
return next((user['id'] for user in users if user['name'] == name), "NONE")
def update_password(self, user_id, old_password, new_password):
"""更新用户密码"""
body = {"user": {"password": new_password, "original_password": old_password}}
api_url = f"{self.url}/{user_id}/password"
response = requests.post(api_url, json=body, headers=self.headers)
return {"更新密码成功": response.status_code} if response.status_code == 204 else response.json()
def delete_user(self, user_id):
"""删除用户"""
response = requests.delete(f"{self.url}/{user_id}", headers=self.headers)
return {"删除用户成功": response.status_code} if response.status_code == 204 else response.json()
# 主函数
if __name__ == '__main__':
controller_ip = "192.168.200.160"
domain = "default"
user = "admin"
password = "000000"
# 获取认证信息
headers = get_auth_token(controller_ip, domain, user, password)
user_manager = UserManager(headers, f"http://{controller_ip}:5000/v3/users")
# 操作用户
print("查询所有用户:", user_manager.get_users())
print("创建用户:", user_manager.create_user("user_demo", "passw0rd", "由Python创建的用户"))
user_id = user_manager.get_user_id("user_demo")
print("获取用户ID:", user_id)
print("更新密码:", user_manager.update_password(user_id, "passw0rd", "newpassw0rd"))
print("删除用户:", user_manager.delete_user(user_id))
使用本代码需要注意以下说明
- 修改主函数下的openstack的连接信息,IP、用户、密码等
- 主函数下面有查询、创建、更新、删除等,需要那个就使用那个函数调用
测试:
- 命令查看当前创建的用户
(myenv) root@controller:~# source /etc/keystone/admin-openrc.sh
(myenv) root@controller:~# openstack user list
+----------------------------------+-----------+
| ID | Name |
+----------------------------------+-----------+
| cb2cba23a7c1446fa424a49a494ed20d | admin |
| b41e7196605f456b8587ed38b759d8ef | glance |
| 8dd4f29f70514918b5879960b17e0645 | placement |
| 79ff6928d66b40a2bd10682651933f6e | nova |
| 1029a0d8098e462da3b80164d4219204 | neutron |
+----------------------------------+-----------+
- 查看端点地址(public接口)和令牌
root@controller:~# openstack endpoint list
+----------------------------------+-----------+--------------+--------------+---------+-----------+-----------------------------+
| ID | Region | Service Name | Service Type | Enabled | Interface | URL |
+----------------------------------+-----------+--------------+--------------+---------+-----------+-----------------------------+
| 33501b74440542188ba76c20b4cbf00e | RegionOne | keystone | identity | True | public | http://controller:5000/v3/ |
| 38b0c4b88aac4e6ba4518fd8ce4204b7 | RegionOne | keystone | identity | True | admin | http://controller:5000/v3/ |
| 38bd876005574b73b3c407f839834bb0 | RegionOne | nova | compute | True | public | http://controller:8774/v2.1 |
| 40ad8b8058ca49a990906e8f9c4b305b | RegionOne | placement | placement | True | admin | http://controller:8778 |
| 4e7c06c7bb434c91996950d71bbec21a | RegionOne | nova | compute | True | admin | http://controller:8774/v2.1 |
| 63391670a8324515ac1d521b97595c7b | RegionOne | keystone | identity | True | internal | http://controller:5000/v3/ |
| 73fda3f889d841c292f8b4b92d991a13 | RegionOne | placement | placement | True | internal | http://controller:8778 |
| 9e47b0e39b8a4486adef88b7cfadaa01 | RegionOne | glance | image | True | admin | http://controller:9292 |
| a478dc96e015440eaaa3c9195ba3cb04 | RegionOne | placement | placement | True | public | http://controller:8778 |
| a4b523fd7d8743ed8000580e189122af | RegionOne | neutron | network | True | public | http://controller:9696 |
| b55a96421d67413796f8b24349ada43f | RegionOne | neutron | network | True | admin | http://controller:9696 |
| b933e8156bfa4c82895a324f1f59a154 | RegionOne | glance | image | True | internal | http://controller:9292 |
| dce16eb156af4f8697be947955aa333a | RegionOne | neutron | network | True | internal | http://controller:9696 |
| e17d5863691342d7a93110222635ff5b | RegionOne | glance | image | True | public | http://controller:9292 |
| f3e70523608546a8b48b5112451b1778 | RegionOne | nova | compute | True | internal | http://controller:8774/v2.1 |
+----------------------------------+-----------+--------------+--------------+---------+-----------+-----------------------------+
root@controller:~# openstack token issue
+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Field | Value |
+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| expires | 2025-01-15T06:46:10+0000 |
| id | gAAAAABnh0uia97V_IBeByhXPgcogzyBox9hhcsjLMxqBKAdge4ZUTU0qRU84kmILuZgq52xhReoWWKnrutMXjiYN-sl6Ijz-7RuoWY220Y0Muk9zssakbueuGBtHISbpc-BqjCtjnt__FMkFPtTPZxWYkci23nFs_zkT2_XS4evcKsgDUPCF7Q |
| project_id | 627a106dda814f3a9f3791e611f16cad |
| user_id | cb2cba23a7c1446fa424a49a494ed20d |
+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
- 使用api调用:这里我只调用会以json格式返回用户的id,名称,所属域等信息
vi keystone_user_api.py
(myenv) root@controller:~# python3 keystone_user_api.py
2025-01-15 03:29:47,504 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 03:29:47,800 http://192.168.200.160:5000 "POST /v3/auth/tokens HTTP/1.1" 201 3476
2025-01-15 03:29:47,801 获取Token值: gAAAAABnhyurRtNO1GyFXHeKnCzQj03CUQ-Zh2rFpVD3w89mV2KMFm8m1A_xovS2yUHGoNJbZhocDLped7i-C-OgFwIWQwvB_eGGrOEADAtHqLao4Imu8YEfKuRJA7QkiVUqymriUYaRswcdreWg1-okITW-3aHH8oGyFKN_JIA_NN8lY7NcKwo
2025-01-15 03:29:47,802 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 03:29:47,951 http://192.168.200.160:5000 "GET /v3/users HTTP/1.1" 200 1299
2025-01-15 03:29:47,951 查询用户响应: {"users": [{"id": "cb2cba23a7c1446fa424a49a494ed20d", "name": "admin", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/cb2cba23a7c1446fa424a49a494ed20d"}}, {"id": "b41e7196605f456b8587ed38b759d8ef", "name": "glance", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/b41e7196605f456b8587ed38b759d8ef"}}, {"id": "8dd4f29f70514918b5879960b17e0645", "name": "placement", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/8dd4f29f70514918b5879960b17e0645"}}, {"id": "79ff6928d66b40a2bd10682651933f6e", "name": "nova", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/79ff6928d66b40a2bd10682651933f6e"}}, {"id": "1029a0d8098e462da3b80164d4219204", "name": "neutron", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/1029a0d8098e462da3b80164d4219204"}}], "links": {"next": null, "self": "http://192.168.200.160:5000/v3/users", "previous": null}}
查询所有用户: {"users": [{"id": "cb2cba23a7c1446fa424a49a494ed20d", "name": "admin", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/cb2cba23a7c1446fa424a49a494ed20d"}}, {"id": "b41e7196605f456b8587ed38b759d8ef", "name": "glance", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/b41e7196605f456b8587ed38b759d8ef"}}, {"id": "8dd4f29f70514918b5879960b17e0645", "name": "placement", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/8dd4f29f70514918b5879960b17e0645"}}, {"id": "79ff6928d66b40a2bd10682651933f6e", "name": "nova", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/79ff6928d66b40a2bd10682651933f6e"}}, {"id": "1029a0d8098e462da3b80164d4219204", "name": "neutron", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/1029a0d8098e462da3b80164d4219204"}}], "links": {"next": null, "self": "http://192.168.200.160:5000/v3/users", "previous": null}}
2025-01-15 03:29:47,952 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 03:29:48,618 http://192.168.200.160:5000 "POST /v3/users HTTP/1.1" 201 312
2025-01-15 03:29:48,618 创建用户响应: {"user": {"description": "\u7531Python\u521b\u5efa\u7684\u7528\u6237", "id": "f801ac380378464ba2a84adfaf7d6084", "name": "user_demo", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/f801ac380378464ba2a84adfaf7d6084"}}}
创建用户: {"user": {"description": "\u7531Python\u521b\u5efa\u7684\u7528\u6237", "id": "f801ac380378464ba2a84adfaf7d6084", "name": "user_demo", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/f801ac380378464ba2a84adfaf7d6084"}}}
2025-01-15 03:29:48,619 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 03:29:48,798 http://192.168.200.160:5000 "GET /v3/users HTTP/1.1" 200 1602
2025-01-15 03:29:48,799 查询用户响应: {"users": [{"id": "cb2cba23a7c1446fa424a49a494ed20d", "name": "admin", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/cb2cba23a7c1446fa424a49a494ed20d"}}, {"id": "b41e7196605f456b8587ed38b759d8ef", "name": "glance", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/b41e7196605f456b8587ed38b759d8ef"}}, {"id": "8dd4f29f70514918b5879960b17e0645", "name": "placement", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/8dd4f29f70514918b5879960b17e0645"}}, {"id": "79ff6928d66b40a2bd10682651933f6e", "name": "nova", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/79ff6928d66b40a2bd10682651933f6e"}}, {"id": "1029a0d8098e462da3b80164d4219204", "name": "neutron", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/1029a0d8098e462da3b80164d4219204"}}, {"description": "\u7531Python\u521b\u5efa\u7684\u7528\u6237", "id": "f801ac380378464ba2a84adfaf7d6084", "name": "user_demo", "domain_id": "default", "enabled": true, "password_expires_at": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/users/f801ac380378464ba2a84adfaf7d6084"}}], "links": {"next": null, "self": "http://192.168.200.160:5000/v3/users", "previous": null}}
获取用户ID: f801ac380378464ba2a84adfaf7d6084
2025-01-15 03:29:48,803 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 03:29:49,502 http://192.168.200.160:5000 "POST /v3/users/f801ac380378464ba2a84adfaf7d6084/password HTTP/1.1" 204 0
更新密码: {'更新密码成功': 204}
2025-01-15 03:29:49,503 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 03:29:49,799 http://192.168.200.160:5000 "DELETE /v3/users/f801ac380378464ba2a84adfaf7d6084 HTTP/1.1" 204 0
删除用户: {'删除用户成功': 204}
编写项目管理py文件
以下为官网示例的项目管理api接口,包含了项目的增删改查等功能:每个接口可对应查看到具体的细节
示例代码如下:
import requests
import json
import logging
# 配置日志
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(message)s')
logger = logging.getLogger(__name__)
# 获取认证Token
def get_auth_token(controller_ip, domain, user, password):
"""
获取 OpenStack 认证Token
:param controller_ip: 控制节点 IP
:param domain: 用户域
:param user: 用户名
:param password: 用户密码
:return: 认证头部信息
"""
try:
url = f"http://{controller_ip}:5000/v3/auth/tokens"
body = {
"auth": {
"identity": {"methods": ["password"], "password": {"user": {"domain": {"name": domain}, "name": user, "password": password}}},
"scope": {"project": {"domain": {"name": domain}, "name": user}}
}
}
response = requests.post(url, json=body, headers={"Content-Type": "application/json"})
token = response.headers['X-Subject-Token']
logger.debug(f"获取Token值: {token}")
return {"X-Auth-Token": token}
except Exception as e:
logger.error(f"获取Token失败: {e}")
exit(1)
# 项目管理类
class ProjectManager:
def __init__(self, headers, url):
self.headers = headers
self.url = url
def create_project(self, name, domain_id, description):
"""创建项目"""
body = {
"project": {
"name": name,
"domain_id": domain_id,
"description": description
}
}
response = requests.post(self.url, json=body, headers=self.headers)
logger.debug(f"创建项目响应: {response.text}")
return response.text
def get_projects(self):
"""获取所有项目"""
response = requests.get(self.url, headers=self.headers)
logger.debug(f"查询项目响应: {response.text}")
return response.text
def get_project_id(self, name):
"""根据项目名获取项目ID"""
projects = json.loads(self.get_projects())['projects']
return next((project['id'] for project in projects if project['name'] == name), "NONE")
def update_project(self, project_id, name=None, description=None, enabled=None):
"""更新项目信息"""
body = {"project": {}}
if name: body["project"]["name"] = name
if description: body["project"]["description"] = description
if enabled is not None: body["project"]["enabled"] = enabled
api_url = f"{self.url}/{project_id}"
response = requests.patch(api_url, json=body, headers=self.headers)
logger.debug(f"更新项目响应: {response.text}")
return response.text
def delete_project(self, project_id):
"""删除项目"""
api_url = f"{self.url}/{project_id}"
response = requests.delete(api_url, headers=self.headers)
return {"删除成功": response.status_code} if response.status_code == 204 else response.json()
# 主函数
if __name__ == '__main__':
# 控制节点认证信息
controller_ip = "192.168.200.160"
domain = "default"
user = "admin"
password = "000000"
# 获取认证头部
headers = get_auth_token(controller_ip, domain, user, password)
# 初始化项目管理
project_manager = ProjectManager(headers, f"http://{controller_ip}:5000/v3/projects")
# 查看所有项目
print("所有项目:", project_manager.get_projects())
# 创建新项目
project_name = "test_project"
description = "由Python创建的测试项目"
domain_id = "default" # 通常是 default
print("创建项目:", project_manager.create_project(project_name, domain_id, description))
# 获取项目ID
project_id = project_manager.get_project_id(project_name)
print(f"项目 '{project_name}' 的ID:", project_id)
# 更新项目
print("更新项目描述:", project_manager.update_project(project_id, description="更新后的项目描述"))
# 删除项目
print("删除项目:", project_manager.delete_project(project_id))
代码说明:
- 查看项目:调用 GET /v3/projects 接口,返回所有项目的列表。
- 创建项目:调用 POST /v3/projects 接口,传入项目名称、域 ID 和描述信息。
- 更新项目:调用 PATCH /v3/projects/{project_id} 接口,可修改名称、描述或启用状态。
- 删除项目:调用 DELETE /v3/projects/{project_id} 接口,删除指定项目
测试:
vi keystone_project_api.py
root@controller:~# python3 keystone_project_api.py
2025-01-15 05:55:51,711 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 05:55:52,031 http://192.168.200.160:5000 "POST /v3/auth/tokens HTTP/1.1" 201 3476
2025-01-15 05:55:52,032 获取Token值: gAAAAABnh03n1TLWv5diNutPqEDbbLN-Sift1q6QNksd8XcaCZGzOE2HRv8Q3cCwmuVXRsbSzfIbOi-sj4SxzbyvbhWv8TdsL3AYauq48Vp0iJ6VHI3gn8EMAJi2-2ncw6vi6M-vSVNs99WOnR76mI-JgQXpmZ6Hal3u4-CTMExXBgcb3adXAu0
2025-01-15 05:55:52,034 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 05:55:52,127 http://192.168.200.160:5000 "GET /v3/projects HTTP/1.1" 200 745
2025-01-15 05:55:52,128 查询项目响应: {"projects": [{"id": "627a106dda814f3a9f3791e611f16cad", "name": "admin", "domain_id": "default", "description": "Bootstrap project for initializing the cloud.", "enabled": true, "parent_id": "default", "is_domain": false, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/projects/627a106dda814f3a9f3791e611f16cad"}}, {"id": "6a166bba90684340a0cd323f7050caa0", "name": "service", "domain_id": "default", "description": "Service Project", "enabled": true, "parent_id": "default", "is_domain": false, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/projects/6a166bba90684340a0cd323f7050caa0"}}], "links": {"next": null, "self": "http://192.168.200.160:5000/v3/projects", "previous": null}}
所有项目: {"projects": [{"id": "627a106dda814f3a9f3791e611f16cad", "name": "admin", "domain_id": "default", "description": "Bootstrap project for initializing the cloud.", "enabled": true, "parent_id": "default", "is_domain": false, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/projects/627a106dda814f3a9f3791e611f16cad"}}, {"id": "6a166bba90684340a0cd323f7050caa0", "name": "service", "domain_id": "default", "description": "Service Project", "enabled": true, "parent_id": "default", "is_domain": false, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/projects/6a166bba90684340a0cd323f7050caa0"}}], "links": {"next": null, "self": "http://192.168.200.160:5000/v3/projects", "previous": null}}
2025-01-15 05:55:52,130 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 05:55:52,318 http://192.168.200.160:5000 "POST /v3/projects HTTP/1.1" 201 360
2025-01-15 05:55:52,319 创建项目响应: {"project": {"id": "3fd0ca143d7545f4b063559511034a5b", "name": "test_project", "domain_id": "default", "description": "\u7531Python\u521b\u5efa\u7684\u6d4b\u8bd5\u9879\u76ee", "enabled": true, "parent_id": "default", "is_domain": false, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/projects/3fd0ca143d7545f4b063559511034a5b"}}}
创建项目: {"project": {"id": "3fd0ca143d7545f4b063559511034a5b", "name": "test_project", "domain_id": "default", "description": "\u7531Python\u521b\u5efa\u7684\u6d4b\u8bd5\u9879\u76ee", "enabled": true, "parent_id": "default", "is_domain": false, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/projects/3fd0ca143d7545f4b063559511034a5b"}}}
2025-01-15 05:55:52,320 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 05:55:52,456 http://192.168.200.160:5000 "GET /v3/projects HTTP/1.1" 200 1093
2025-01-15 05:55:52,457 查询项目响应: {"projects": [{"id": "3fd0ca143d7545f4b063559511034a5b", "name": "test_project", "domain_id": "default", "description": "\u7531Python\u521b\u5efa\u7684\u6d4b\u8bd5\u9879\u76ee", "enabled": true, "parent_id": "default", "is_domain": false, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/projects/3fd0ca143d7545f4b063559511034a5b"}}, {"id": "627a106dda814f3a9f3791e611f16cad", "name": "admin", "domain_id": "default", "description": "Bootstrap project for initializing the cloud.", "enabled": true, "parent_id": "default", "is_domain": false, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/projects/627a106dda814f3a9f3791e611f16cad"}}, {"id": "6a166bba90684340a0cd323f7050caa0", "name": "service", "domain_id": "default", "description": "Service Project", "enabled": true, "parent_id": "default", "is_domain": false, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/projects/6a166bba90684340a0cd323f7050caa0"}}], "links": {"next": null, "self": "http://192.168.200.160:5000/v3/projects", "previous": null}}
项目 'test_project' 的ID: 3fd0ca143d7545f4b063559511034a5b
2025-01-15 05:55:52,458 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 05:55:52,605 http://192.168.200.160:5000 "PATCH /v3/projects/3fd0ca143d7545f4b063559511034a5b HTTP/1.1" 200 367
2025-01-15 05:55:52,605 更新项目响应: {"project": {"id": "3fd0ca143d7545f4b063559511034a5b", "name": "test_project", "domain_id": "default", "description": "\u66f4\u65b0\u540e\u7684\u9879\u76ee\u63cf\u8ff0", "enabled": true, "parent_id": "default", "is_domain": false, "tags": [], "extra": {}, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/projects/3fd0ca143d7545f4b063559511034a5b"}}}
更新项目描述: {"project": {"id": "3fd0ca143d7545f4b063559511034a5b", "name": "test_project", "domain_id": "default", "description": "\u66f4\u65b0\u540e\u7684\u9879\u76ee\u63cf\u8ff0", "enabled": true, "parent_id": "default", "is_domain": false, "tags": [], "extra": {}, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/projects/3fd0ca143d7545f4b063559511034a5b"}}}
2025-01-15 05:55:52,606 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 05:55:52,802 http://192.168.200.160:5000 "DELETE /v3/projects/3fd0ca143d7545f4b063559511034a5b HTTP/1.1" 204 0
删除项目: {'删除成功': 204}
编写域管理py文件
以下为官网示例的域管理api接口,包含了域的增删改查等功能:每个接口的detail可对应查看到具体的细节
示例代码如下:
import requests
import json
import logging
# 配置日志
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(message)s')
logger = logging.getLogger(__name__)
# 获取认证Token
def get_auth_token(controller_ip, domain, user, password):
"""
获取 OpenStack 认证Token
:param controller_ip: 控制节点 IP
:param domain: 用户域
:param user: 用户名
:param password: 用户密码
:return: 认证头部信息
"""
try:
url = f"http://{controller_ip}:5000/v3/auth/tokens"
body = {
"auth": {
"identity": {"methods": ["password"], "password": {"user": {"domain": {"name": domain}, "name": user, "password": password}}},
"scope": {"project": {"domain": {"name": domain}, "name": user}}
}
}
response = requests.post(url, json=body, headers={"Content-Type": "application/json"})
token = response.headers['X-Subject-Token']
logger.debug(f"获取Token值: {token}")
return {"X-Auth-Token": token}
except Exception as e:
logger.error(f"获取Token失败: {e}")
exit(1)
# 域管理类
class DomainManager:
def __init__(self, headers, url):
self.headers = headers
self.url = url
def create_domain(self, name, description, enabled=True):
"""创建域"""
body = {
"domain": {
"name": name,
"description": description,
"enabled": enabled
}
}
response = requests.post(self.url, json=body, headers=self.headers)
logger.debug(f"创建域响应: {response.text}")
return response.text
def get_domains(self):
"""获取所有域"""
response = requests.get(self.url, headers=self.headers)
logger.debug(f"查询域响应: {response.text}")
return response.text
def get_domain_id(self, name):
"""根据域名获取域ID"""
domains = json.loads(self.get_domains())['domains']
return next((domain['id'] for domain in domains if domain['name'] == name), "NONE")
def update_domain(self, domain_id, name=None, description=None, enabled=None):
"""更新域信息"""
body = {"domain": {}}
if name: body["domain"]["name"] = name
if description: body["domain"]["description"] = description
if enabled is not None: body["domain"]["enabled"] = enabled
api_url = f"{self.url}/{domain_id}"
response = requests.patch(api_url, json=body, headers=self.headers)
logger.debug(f"更新域响应: {response.text}")
return response.text
def delete_domain(self, domain_id):
"""删除域"""
api_url = f"{self.url}/{domain_id}"
response = requests.delete(api_url, headers=self.headers)
return {"删除成功": response.status_code} if response.status_code == 204 else response.json()
# 主函数
if __name__ == '__main__':
# 控制节点认证信息
controller_ip = "192.168.200.160"
domain = "default"
user = "admin"
password = "000000"
# 获取认证头部
headers = get_auth_token(controller_ip, domain, user, password)
# 初始化域管理
domain_manager = DomainManager(headers, f"http://{controller_ip}:5000/v3/domains")
# 查看所有域
print("所有域:", domain_manager.get_domains())
# 创建新域
domain_name = "test_domain"
description = "由Python创建的测试域"
print("创建域:", domain_manager.create_domain(domain_name, description))
# 获取域ID
domain_id = domain_manager.get_domain_id(domain_name)
print(f"域 '{domain_name}' 的ID:", domain_id)
# 更新域
print("更新域描述:", domain_manager.update_domain(domain_id, description="更新后的域描述", enabled=False))
# 删除域
print("删除域:", domain_manager.delete_domain(domain_id))
代码说明:
- 查看域:调用 GET /v3/domains 接口,返回所有域的信息。
- 创建域:调用 POST /v3/domains 接口,传入域的名称、描述和启用状态。
- 更新域:调用 PATCH /v3/domains/{domain_id} 接口,可以更新域的名称、描述和启用状态。
- 删除域:调用 DELETE /v3/domains/{domain_id} 接口,删除指定的域。
测试
vi keystone_domain_api.py
root@controller:~# python3 keystone_domain_api.py
2025-01-15 06:08:16,839 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 06:08:17,135 http://192.168.200.160:5000 "POST /v3/auth/tokens HTTP/1.1" 201 3476
2025-01-15 06:08:17,136 获取Token值: gAAAAABnh1DRgfxySemhvN0q-xe5lqcQWJT4IBzrv0qzf-PfG5wcsps5MhuyVb3wEnFRQzykbsE2iMOsrL6iMOx5R92rZcd7AH3Z9P_F35ULgXV7X0HLH1MRTL7EFWHidguypC8f3tYi3aFedwkKvR5Uebf20outs9XAn0ZQ3xt63ezL8VTH0ew
2025-01-15 06:08:17,137 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 06:08:17,294 http://192.168.200.160:5000 "GET /v3/domains HTTP/1.1" 200 295
2025-01-15 06:08:17,294 查询域响应: {"domains": [{"id": "default", "name": "Default", "description": "The default domain", "enabled": true, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/domains/default"}}], "links": {"next": null, "self": "http://192.168.200.160:5000/v3/domains", "previous": null}}
所有域: {"domains": [{"id": "default", "name": "Default", "description": "The default domain", "enabled": true, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/domains/default"}}], "links": {"next": null, "self": "http://192.168.200.160:5000/v3/domains", "previous": null}}
2025-01-15 06:08:17,295 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 06:08:17,765 http://192.168.200.160:5000 "POST /v3/domains HTTP/1.1" 201 283
2025-01-15 06:08:17,765 创建域响应: {"domain": {"id": "f7b409d740f74a3e82dab9fe91ab102c", "name": "test_domain", "description": "\u7531Python\u521b\u5efa\u7684\u6d4b\u8bd5\u57df", "enabled": true, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/domains/f7b409d740f74a3e82dab9fe91ab102c"}}}
创建域: {"domain": {"id": "f7b409d740f74a3e82dab9fe91ab102c", "name": "test_domain", "description": "\u7531Python\u521b\u5efa\u7684\u6d4b\u8bd5\u57df", "enabled": true, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/domains/f7b409d740f74a3e82dab9fe91ab102c"}}}
2025-01-15 06:08:17,766 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 06:08:17,926 http://192.168.200.160:5000 "GET /v3/domains HTTP/1.1" 200 567
2025-01-15 06:08:17,926 查询域响应: {"domains": [{"id": "default", "name": "Default", "description": "The default domain", "enabled": true, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/domains/default"}}, {"id": "f7b409d740f74a3e82dab9fe91ab102c", "name": "test_domain", "description": "\u7531Python\u521b\u5efa\u7684\u6d4b\u8bd5\u57df", "enabled": true, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/domains/f7b409d740f74a3e82dab9fe91ab102c"}}], "links": {"next": null, "self": "http://192.168.200.160:5000/v3/domains", "previous": null}}
域 'test_domain' 的ID: f7b409d740f74a3e82dab9fe91ab102c
2025-01-15 06:08:17,927 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 06:08:18,088 http://192.168.200.160:5000 "PATCH /v3/domains/f7b409d740f74a3e82dab9fe91ab102c HTTP/1.1" 200 278
2025-01-15 06:08:18,088 更新域响应: {"domain": {"id": "f7b409d740f74a3e82dab9fe91ab102c", "name": "test_domain", "description": "\u66f4\u65b0\u540e\u7684\u57df\u63cf\u8ff0", "enabled": false, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/domains/f7b409d740f74a3e82dab9fe91ab102c"}}}
更新域描述: {"domain": {"id": "f7b409d740f74a3e82dab9fe91ab102c", "name": "test_domain", "description": "\u66f4\u65b0\u540e\u7684\u57df\u63cf\u8ff0", "enabled": false, "tags": [], "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/domains/f7b409d740f74a3e82dab9fe91ab102c"}}}
2025-01-15 06:08:18,090 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 06:08:18,359 http://192.168.200.160:5000 "DELETE /v3/domains/f7b409d740f74a3e82dab9fe91ab102c HTTP/1.1" 204 0
编写角色管理py文件
以下为官网示角色的域管理api接口,包含了角色的增删改查等功能:每个接口的detail可对应查看到具体的细节
示例代码:
import requests
import json
import logging
# 配置日志
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(message)s')
logger = logging.getLogger(__name__)
# 获取认证Token
def get_auth_token(controller_ip, domain, user, password):
"""
获取 OpenStack 认证Token
:param controller_ip: 控制节点 IP
:param domain: 用户域
:param user: 用户名
:param password: 用户密码
:return: 认证头部信息
"""
try:
url = f"http://{controller_ip}:5000/v3/auth/tokens"
body = {
"auth": {
"identity": {"methods": ["password"], "password": {"user": {"domain": {"name": domain}, "name": user, "password": password}}},
"scope": {"project": {"domain": {"name": domain}, "name": user}}
}
}
response = requests.post(url, json=body, headers={"Content-Type": "application/json"})
token = response.headers['X-Subject-Token']
logger.debug(f"获取Token值: {token}")
return {"X-Auth-Token": token}
except Exception as e:
logger.error(f"获取Token失败: {e}")
exit(1)
# 角色管理类
class RoleManager:
def __init__(self, headers, url):
self.headers = headers
self.url = url
def create_role(self, name):
"""创建角色"""
body = {"role": {"name": name}}
response = requests.post(self.url, json=body, headers=self.headers)
logger.debug(f"创建角色响应: {response.text}")
return response.text
def get_roles(self):
"""获取所有角色"""
response = requests.get(self.url, headers=self.headers)
logger.debug(f"查询角色响应: {response.text}")
return response.text
def get_role_id(self, name):
"""根据角色名称获取角色ID"""
roles = json.loads(self.get_roles())['roles']
return next((role['id'] for role in roles if role['name'] == name), "NONE")
def update_role(self, role_id, name):
"""更新角色名称"""
body = {"role": {"name": name}}
api_url = f"{self.url}/{role_id}"
response = requests.patch(api_url, json=body, headers=self.headers)
logger.debug(f"更新角色响应: {response.text}")
return response.text
def delete_role(self, role_id):
"""删除角色"""
api_url = f"{self.url}/{role_id}"
response = requests.delete(api_url, headers=self.headers)
if response.status_code == 204:
return {"删除成功": response.status_code}
return response.json()
# 主函数
if __name__ == '__main__':
# 控制节点认证信息
controller_ip = "192.168.200.160"
domain = "default"
user = "admin"
password = "000000"
# 获取认证头部
headers = get_auth_token(controller_ip, domain, user, password)
# 初始化角色管理
role_manager = RoleManager(headers, f"http://{controller_ip}:5000/v3/roles")
# 查询所有角色
print("所有角色:", role_manager.get_roles())
# 创建新角色
role_name = "test_role"
print("创建角色:", role_manager.create_role(role_name))
# 获取角色ID
role_id = role_manager.get_role_id(role_name)
print(f"角色 '{role_name}' 的ID:", role_id)
# 更新角色名称
new_role_name = "updated_test_role"
print("更新角色名称:", role_manager.update_role(role_id, new_role_name))
# 删除角色
print("删除角色:", role_manager.delete_role(role_id))
角色操作:
- 查看角色:调用 GET /v3/roles 接口,列出所有角色。
- 创建角色:调用 POST /v3/roles 接口,传入角色名称。
- 更新角色:调用 PATCH /v3/roles/{role_id} 接口,更新角色的名称。
- 删除角色:调用 DELETE /v3/roles/{role_id} 接口,删除指定角色
测试
vi keystone_role_api.py
root@controller:~# python3 keystone_role_api.py
2025-01-15 06:16:40,785 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 06:16:41,106 http://192.168.200.160:5000 "POST /v3/auth/tokens HTTP/1.1" 201 3476
2025-01-15 06:16:41,106 获取Token值: gAAAAABnh1LJE9IjD7hKQqxEwLZye0vX-71YObS3Aak4vOyKDN24T56SGXH3370mYXhrplzAIAVqIQXek3gHReQINYuxHsjtYGkiam72SJkP8w6dPw9f1cQKltl1_bndfy3CWon28ePYmUnOmeV_tY7vYXXDwwaCrvKdq1XIMp_KMuYnrLqDVgw
2025-01-15 06:16:41,107 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 06:16:41,381 http://192.168.200.160:5000 "GET /v3/roles HTTP/1.1" 200 1234
2025-01-15 06:16:41,381 查询角色响应: {"roles": [{"id": "3e15391b2c87436a82de12fbb7982681", "name": "reader", "domain_id": null, "description": null, "options": {"immutable": true}, "links": {"self": "http://192.168.200.160:5000/v3/roles/3e15391b2c87436a82de12fbb7982681"}}, {"id": "522d6975aaab42608d9be994ee25b498", "name": "service", "domain_id": null, "description": null, "options": {"immutable": true}, "links": {"self": "http://192.168.200.160:5000/v3/roles/522d6975aaab42608d9be994ee25b498"}}, {"id": "749691b26ec14b07a54e675ff7ec6184", "name": "manager", "domain_id": null, "description": null, "options": {"immutable": true}, "links": {"self": "http://192.168.200.160:5000/v3/roles/749691b26ec14b07a54e675ff7ec6184"}}, {"id": "8b70169a0e534455902ed185b6791059", "name": "admin", "domain_id": null, "description": null, "options": {"immutable": true}, "links": {"self": "http://192.168.200.160:5000/v3/roles/8b70169a0e534455902ed185b6791059"}}, {"id": "e6ff70a44a774213a729322e0530399b", "name": "member", "domain_id": null, "description": null, "options": {"immutable": true}, "links": {"self": "http://192.168.200.160:5000/v3/roles/e6ff70a44a774213a729322e0530399b"}}], "links": {"next": null, "self": "http://192.168.200.160:5000/v3/roles", "previous": null}}
所有角色: {"roles": [{"id": "3e15391b2c87436a82de12fbb7982681", "name": "reader", "domain_id": null, "description": null, "options": {"immutable": true}, "links": {"self": "http://192.168.200.160:5000/v3/roles/3e15391b2c87436a82de12fbb7982681"}}, {"id": "522d6975aaab42608d9be994ee25b498", "name": "service", "domain_id": null, "description": null, "options": {"immutable": true}, "links": {"self": "http://192.168.200.160:5000/v3/roles/522d6975aaab42608d9be994ee25b498"}}, {"id": "749691b26ec14b07a54e675ff7ec6184", "name": "manager", "domain_id": null, "description": null, "options": {"immutable": true}, "links": {"self": "http://192.168.200.160:5000/v3/roles/749691b26ec14b07a54e675ff7ec6184"}}, {"id": "8b70169a0e534455902ed185b6791059", "name": "admin", "domain_id": null, "description": null, "options": {"immutable": true}, "links": {"self": "http://192.168.200.160:5000/v3/roles/8b70169a0e534455902ed185b6791059"}}, {"id": "e6ff70a44a774213a729322e0530399b", "name": "member", "domain_id": null, "description": null, "options": {"immutable": true}, "links": {"self": "http://192.168.200.160:5000/v3/roles/e6ff70a44a774213a729322e0530399b"}}], "links": {"next": null, "self": "http://192.168.200.160:5000/v3/roles", "previous": null}}
2025-01-15 06:16:41,384 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 06:16:41,538 http://192.168.200.160:5000 "POST /v3/roles HTTP/1.1" 201 221
2025-01-15 06:16:41,538 创建角色响应: {"role": {"id": "51f751d675ce4df78c50016992b38198", "name": "test_role", "domain_id": null, "description": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/roles/51f751d675ce4df78c50016992b38198"}}}
创建角色: {"role": {"id": "51f751d675ce4df78c50016992b38198", "name": "test_role", "domain_id": null, "description": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/roles/51f751d675ce4df78c50016992b38198"}}}
2025-01-15 06:16:41,540 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 06:16:41,682 http://192.168.200.160:5000 "GET /v3/roles HTTP/1.1" 200 1446
2025-01-15 06:16:41,682 查询角色响应: {"roles": [{"id": "3e15391b2c87436a82de12fbb7982681", "name": "reader", "domain_id": null, "description": null, "options": {"immutable": true}, "links": {"self": "http://192.168.200.160:5000/v3/roles/3e15391b2c87436a82de12fbb7982681"}}, {"id": "51f751d675ce4df78c50016992b38198", "name": "test_role", "domain_id": null, "description": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/roles/51f751d675ce4df78c50016992b38198"}}, {"id": "522d6975aaab42608d9be994ee25b498", "name": "service", "domain_id": null, "description": null, "options": {"immutable": true}, "links": {"self": "http://192.168.200.160:5000/v3/roles/522d6975aaab42608d9be994ee25b498"}}, {"id": "749691b26ec14b07a54e675ff7ec6184", "name": "manager", "domain_id": null, "description": null, "options": {"immutable": true}, "links": {"self": "http://192.168.200.160:5000/v3/roles/749691b26ec14b07a54e675ff7ec6184"}}, {"id": "8b70169a0e534455902ed185b6791059", "name": "admin", "domain_id": null, "description": null, "options": {"immutable": true}, "links": {"self": "http://192.168.200.160:5000/v3/roles/8b70169a0e534455902ed185b6791059"}}, {"id": "e6ff70a44a774213a729322e0530399b", "name": "member", "domain_id": null, "description": null, "options": {"immutable": true}, "links": {"self": "http://192.168.200.160:5000/v3/roles/e6ff70a44a774213a729322e0530399b"}}], "links": {"next": null, "self": "http://192.168.200.160:5000/v3/roles", "previous": null}}
角色 'test_role' 的ID: 51f751d675ce4df78c50016992b38198
2025-01-15 06:16:41,683 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 06:16:41,853 http://192.168.200.160:5000 "PATCH /v3/roles/51f751d675ce4df78c50016992b38198 HTTP/1.1" 200 229
2025-01-15 06:16:41,853 更新角色响应: {"role": {"id": "51f751d675ce4df78c50016992b38198", "name": "updated_test_role", "domain_id": null, "description": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/roles/51f751d675ce4df78c50016992b38198"}}}
更新角色名称: {"role": {"id": "51f751d675ce4df78c50016992b38198", "name": "updated_test_role", "domain_id": null, "description": null, "options": {}, "links": {"self": "http://192.168.200.160:5000/v3/roles/51f751d675ce4df78c50016992b38198"}}}
2025-01-15 06:16:41,854 Starting new HTTP connection (1): 192.168.200.160:5000
2025-01-15 06:16:41,975 http://192.168.200.160:5000 "DELETE /v3/roles/51f751d675ce4df78c50016992b38198 HTTP/1.1" 204 0
删除角色: {'删除成功': 204}
root@controller:~#