| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827 |
- # encoding: utf-8
- from __future__ import unicode_literals
- import os
- import base64
- from lib import k3s
- from . import ansible
- from . import utils
- from . import consts
- from .color import RB as Red
- from .k3s import is_using_k3s
- # Import IPv6 support functions
- try:
- import run
- from run import match_ipaddr
- except ImportError:
- # Fallback function if import fails
- def match_ipaddr(ip_str):
- import re
- # Simple IPv4 regex
- ipv4_pattern = r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'
- if re.match(ipv4_pattern, ip_str):
- return (True, consts.IP_TYPE_IPV4)
- # Simple IPv6 regex (basic check)
- if ':' in ip_str:
- return (True, consts.IP_TYPE_IPV6)
- return (False, None)
- GROUP_MARIADB_NODE = "mariadb_node"
- GROUP_MARIADB_HA_NODES = "mariadb_ha_nodes"
- GROUP_CLICKHOUSE_NODE = "clickhouse_node"
- GROUP_REGISTRY_NODE = "registry_node"
- GROUP_PRIMARY_MASTER_NODE = "primary_master_node"
- GROUP_MASTER_NODES = "master_nodes"
- GROUP_WORKER_NODES = "worker_nodes"
- GROUP_NODES = "nodes"
- KEY_HOSTNAME = 'hostname'
- KEY_ONECLOUD_VERSION = 'onecloud_version'
- KEY_OPERATOR_VERSION = 'operator_version'
- KEY_ONECLOUD_MAJOR_VERSION = 'onecloud_major_version'
- KEY_PRODUCT_VERSION = 'product_version'
- KEY_AS_HOST = 'as_host'
- KEY_AS_HOST_ON_VM = 'as_host_on_vm'
- KEY_EXTRA_PACKAGES = 'extra_packages'
- KEY_IMAGE_REPOSITORY = 'image_repository'
- KEY_K8S_CONTROLPLANE_HOST = 'k8s_controlplane_host'
- KEY_K8S_OR_K3S = 'env_k8s_or_k3s'
- KEY_K3S_API_ENDPOINT = "api_endpoint"
- KEY_K3S_API_PORT = "api_port"
- KEY_K3S_AIRGAP_DIR = "airgap_dir"
- KEY_K3S_VERSION = "k3s_version"
- VAL_K3S_VERSION = k3s.VERSION_V1_28_5_K3S_1
- KEY_K3S_TOKEN = "token"
- VAL_K3S_TOKEN = "mytoken@yunionio"
- KEY_STACK_FULLSTACK = 'FullStack'
- KEY_STACK_EDGE = 'Edge'
- KEY_STACK_LIGHT_EDGE = 'LightEdge'
- KEY_STACK_CMP = 'CMP'
- KEY_STACK_AI = 'AI'
- KEY_STACK_LIST = [KEY_STACK_FULLSTACK, KEY_STACK_EDGE, KEY_STACK_LIGHT_EDGE, KEY_STACK_CMP, KEY_STACK_AI]
- KEY_TARGET_EDITION = 'TARGET_EDITION'
- KEY_USER_DNS = 'user_dns'
- KEY_REGION = "region"
- KEY_ZONE = "zone"
- KEY_ENABLE_CONTAINERD = "enable_containerd"
- KEY_HOST_NETWORKS = "host_networks"
- KEY_DISK_PATHS = "disk_paths"
- KEY_PRIMARY_MASTER_NODE_IP = "primary_master_node_ip"
- def load_config(config_file):
- import yaml
- with open(config_file) as f:
- config = Config(yaml.safe_load(f))
- return OcbootConfig(config)
- def get_ansible_global_vars_by_cluster(cluster):
- vars = get_ansible_global_vars(
- cluster.get_current_version(),
- cluster.is_using_k3s())
- return vars
- def get_ansible_global_vars(version, _is_using_k3s=None):
- if _is_using_k3s is None:
- _is_using_k3s = is_using_k3s()
- major_version = utils.get_major_version(version)
- vars = {
- KEY_ONECLOUD_VERSION: version,
- KEY_ONECLOUD_MAJOR_VERSION: major_version,
- KEY_EXTRA_PACKAGES: [],
- KEY_K3S_VERSION: VAL_K3S_VERSION,
- KEY_K3S_AIRGAP_DIR: k3s.GET_AIRGAP_DIR(),
- KEY_K3S_TOKEN: VAL_K3S_TOKEN,
- KEY_K8S_OR_K3S: 'k3s' if _is_using_k3s else 'k8s', # for path, eg: utils/k8s/kubelet or utils/k3s/kubelet,
- }
- # set yunion_qemu_package for pre released version
- yunion_qemu_package = 'yunion-qemu-4.2.0'
- extra_packages = []
- if utils.is_below_v3_9(version):
- yunion_qemu_package = 'yunion-qemu-2.12.1'
- if yunion_qemu_package:
- vars['yunion_qemu_package'] = yunion_qemu_package
- vars[KEY_EXTRA_PACKAGES] = extra_packages
- return vars
- class OcbootConfig(object):
- def __init__(self, config):
- self.config = config
- self.bastion_host = self.load_bastion_host(config)
- self.mariadb_config = self._fetch_conf(MariadbConfig)
- self.mariadb_ha_config = self._fetch_conf(MariadbHAConfig)
- self.clickhouse_config = self._fetch_conf(ClickhouseConfig)
- self.registry_config = self._fetch_conf(RegistryConfig)
- self.primary_master_config = self._fetch_conf(PrimaryMasterConfig)
- self.master_config = self._fetch_conf(MasterConfig)
- if self.master_config:
- self.master_config.set_primary_master_config(self.primary_master_config)
- self.worker_config = self._fetch_conf(WorkerConfig)
- if self.worker_config:
- self.worker_config.set_primary_master_config(self.primary_master_config)
- if self.mariadb_config and self.mariadb_ha_config:
- raise Exception("mariadb_node and mariadb_ha_nodes can't coexist in config")
- def load_bastion_host(self, config):
- bastion_config = config.get('bastion_host', None)
- if not bastion_config:
- return None
- bastion_config = Config(bastion_config)
- host = bastion_config.ensure_get('host', 'hostname')
- user = bastion_config.get('user', 'root')
- return ansible.AnsibleBastionHost(host, user)
- def is_iso_join_mode(self):
- worker_nodes = self.config.get('worker_nodes', None)
- master_nodes = self.config.get('master_nodes', None)
- if worker_nodes and worker_nodes.get('join_token', None):
- return True
- if master_nodes and master_nodes.get('join_token', None):
- return True
- return False
- def get_primary_master_ssh_port(self):
- if self.is_iso_join_mode():
- return
- return self.primary_master_config.node.port
- def is_controller_node(self):
- if self.primary_master_config:
- return True
- if self.master_config:
- return True
- return False
- def _fetch_conf(self, config_cls):
- group = config_cls.get_group()
- group_config = self.config.get(group, None)
- if not group_config:
- return None
- if group in [GROUP_MASTER_NODES, GROUP_WORKER_NODES]:
- if not group_config.get('controlplane_ssh_port', None):
- group_config['controlplane_ssh_port'] = self.get_primary_master_ssh_port()
- return config_cls(Config(group_config), self.bastion_host)
- def get_onecloud_version(self):
- for node in [self.mariadb_config, self.mariadb_ha_config, self.clickhouse_config, self.registry_config, self.primary_master_config, self.master_config, self.worker_config]:
- if not node:
- continue
- version = getattr(node, KEY_ONECLOUD_VERSION, None)
- if version:
- return version
- raise Exception("get attr onecloud_version error")
- def ansible_global_vars(self):
- return get_ansible_global_vars(self.get_onecloud_version())
- def get_ansible_inventory(self):
- return ansible.get_inventory_config(
- self.mariadb_config,
- self.mariadb_ha_config,
- self.clickhouse_config,
- self.registry_config,
- self.primary_master_config,
- self.master_config,
- self.worker_config)
- def generate_inventory_file(self):
- content = self.get_ansible_inventory()
- yaml_content = utils.to_yaml(content)
- filepath = './host_inventory.yml'
- with open(filepath, 'w') as f:
- f.write(yaml_content)
- return filepath
- def get_login_info(self):
- if self.primary_master_config is None:
- return None
- p_master_config = self.primary_master_config
- frontend_ip = p_master_config.controlplane_host
- user = p_master_config.onecloud_user
- password = p_master_config.onecloud_user_password
- if frontend_ip is None:
- raise Exception("Not found controlplane_host in config")
- if user is None:
- raise Exception("Not found onecloud_user in config")
- if password is None:
- raise Exception("Not found onecloud_user_password in config")
- # 处理IPv6地址:为URL显示添加方括号
- from run import _match_ipv6addr
- if _match_ipv6addr(frontend_ip):
- display_ip = f"[{frontend_ip}]"
- else:
- display_ip = frontend_ip
- return (display_ip, user, password)
- def is_using_ee(self):
- return self.primary_master_config.use_ee
- class ConfigNotFoundException(Exception):
- def __init__(self, config, what):
- self.config = config
- self.what = what
- def __str__(self):
- return "not found '%s' in config %s" % (self.what, self.config)
- class Config(object):
- def __init__(self, config):
- self.config = config
- def get_config(self, group):
- config = self.ensure_get(group)
- return Config(config)
- def get(self, key, default):
- return self.config.get(key, default)
- def __getattr__(self, item):
- return self.config[item]
- def ensure_get(self, key, alter_key=None):
- val = self.get(key, None)
- if val:
- return val
- if alter_key:
- val = self.get(alter_key, None)
- if not val:
- what = key
- if alter_key:
- what = '%s or %s' % (key, alter_key)
- raise ConfigNotFoundException(self.config, what)
- return val
- class Node(object):
- def __init__(self, config):
- self.use_local = config.get('use_local', False)
- self.host = '127.0.0.1' if self.use_local else config.ensure_get('host', 'hostname')
- self.user = config.get('user', 'root')
- self.port = config.get('port', 22)
- self.host_networks = config.get(KEY_HOST_NETWORKS, None)
- if isinstance(self.host_networks, str):
- self.host_networks = [self.host_networks]
- self.disk_paths = config.get(KEY_DISK_PATHS, None)
- self.enable_hugepage = config.get('enable_hugepage', True)
- self.node_ip = config.get('node_ip', None)
- if not self.node_ip:
- self.node_ip = self.host
- self.bastion_host = None
- self.vrrp_priority = config.get('vrrp_priority', 0)
- self.vrrp_interface = config.get('vrrp_interface', None)
- self.vrrp_vip = config.get('vrrp_vip', None)
- self.vrrp_router_id = config.get('vrrp_router_id', None)
- def get_host(self):
- return self.host
- def with_bastion(self, bastion_host):
- self.bastion_host = bastion_host
- return self
- def ansible_host_vars(self):
- vars = {
- 'ansible_host': self.host,
- 'ansible_user': self.user,
- 'ansible_port': self.port,
- }
- if self.user != 'root':
- vars['ansible_become'] = 'yes'
- if self.bastion_host:
- vars['ansible_ssh_common_args'] = self.bastion_host.to_option()
- if self.host != "127.0.0.1":
- vars['node_ip'] = self.node_ip
- if self.use_local:
- vars['ansible_connection'] = 'local'
- if self.host_networks:
- vars[KEY_HOST_NETWORKS] = self.host_networks
- if self.disk_paths:
- vars[KEY_DISK_PATHS] = self.disk_paths
- if self.enable_hugepage:
- vars['enable_hugepage'] = self.enable_hugepage
- if self.vrrp_interface or self.vrrp_vip:
- vars['vrrp_vip'] = self.vrrp_vip
- vars['vrrp_interface'] = self.vrrp_interface
- vars['vrrp_priority'] = self.vrrp_priority
- vars['vrrp_router_id'] = self.vrrp_router_id
- return vars
- def __str__(self):
- ret = self.host
- if self.host != "127.0.0.1":
- ret = "%s node_ip=%s" % (ret, self.node_ip)
- if self.use_local:
- ret = "%s ansible_connection=local" % ret
- if self.user is not None:
- ret = "%s ansible_user=%s" % (ret, self.user)
- if self.host_networks is not None:
- ret = "%s host_networks=%s" % (ret, self.host_networks)
- return ret
- class MariadbConfig(object):
- def __init__(self, config, bastion_host=None):
- self.node = Node(config).with_bastion(bastion_host)
- self.db_user = config.get('db_user', 'root')
- self.db_password = config.ensure_get('db_password')
- self.db_port = config.get('db_port', 3306)
- @classmethod
- def get_group(cls):
- return GROUP_MARIADB_NODE
- def get_nodes(self):
- return [self.node]
- def ansible_vars(self):
- return {
- "db_user": self.db_user,
- "db_password": self.db_password,
- "db_port": self.db_port,
- "db_host": self.node.host,
- }
- class MariadbHAConfig(object):
- def __init__(self, config, bastion_host=None):
- self.nodes = get_nodes(config, bastion_host)
- self.db_user = config.get('db_user', 'root')
- self.db_password = config.ensure_get('db_password')
- self.db_port = config.get('db_port', 3306)
- self.db_vip = config.get('db_vip', None)
- self.db_nic = config.get('db_nic', None)
- @classmethod
- def get_group(cls):
- return GROUP_MARIADB_HA_NODES
- def get_nodes(self):
- return self.nodes
- def ansible_vars(self):
- vars = {
- "db_user": self.db_user,
- "db_password": self.db_password,
- "db_port": self.db_port,
- }
- if self.db_vip:
- vars['db_vip'] = self.db_vip
- vars['db_nic'] = self.db_nic
- return vars
- class ClickhouseConfig(object):
- def __init__(self, config, bastion_host=None):
- self.node = Node(config).with_bastion(bastion_host)
- self.ch_password = config.ensure_get('ch_password')
- self.ch_port = config.get('ch_port', 9000)
- @classmethod
- def get_group(cls):
- return GROUP_CLICKHOUSE_NODE
- def get_nodes(self):
- return [self.node]
- def ansible_vars(self):
- vars = {
- "ch_password": self.ch_password,
- "ch_port": self.ch_port,
- }
- return vars
- class RegistryConfig(object):
- def __init__(self, config, bastion_host=None):
- self.node = Node(config).with_bastion(bastion_host)
- self.port = config.get('port', '5000')
- self.root_dir = config.get('root_dir', '/opt/registry')
- @classmethod
- def get_group(cls):
- return GROUP_REGISTRY_NODE
- def get_nodes(self):
- return [self.node]
- def ansible_vars(self):
- return {
- "listen_port": self.port,
- "root_dir": self.root_dir,
- }
- class OnecloudConfig(object):
- def __init__(self, config):
- self.controlplane_host = config.ensure_get('controlplane_host')
- self.controlplane_port = config.get('controlplane_port', '6443')
- self.as_host = config.get('as_host', None)
- self.as_host_on_vm = config.get('as_host_on_vm', None)
- self.registry_mirrors = config.get('registry_mirrors', [])
- self.insecure_registries = config.get('insecure_registries', [])
- self.skip_docker_config = config.get('skip_docker_config', False)
- self.node_ip = config.get('node_ip', None)
- self.onecloud_version = config.get(KEY_ONECLOUD_VERSION, None)
- self.high_availability = config.get('high_availability', False)
- self.high_availability_vip = None
- self.keepalived_version_tag = None
- self.keepalived_password = None
- default_keepalived_version_tag = 'v2.0.29' if is_using_k3s() else 'v2.0.25'
- if self.high_availability:
- self.high_availability_vip = self.controlplane_host
- self.keepalived_password = base64.b64encode(self.high_availability_vip.encode('ascii'))[0:8].decode()
- # 计算 keepalived_router_id,支持 IPv4 和 IPv6
- if ':' in self.high_availability_vip:
- # IPv6 地址:使用哈希值计算 router_id
- import hashlib
- hash_value = hashlib.md5(self.high_availability_vip.encode()).hexdigest()
- self.keepalived_router_id = int(hash_value[:8], 16) % 255
- else:
- # IPv4 地址:使用原来的逻辑
- self.keepalived_router_id = int(self.high_availability_vip.replace('.', '')) % 255
- if self.keepalived_router_id == 0:
- self.keepalived_router_id = 100
- self.keepalived_version_tag = config.get('keepalived_version_tag', default_keepalived_version_tag)
- self.iso_install_mode = config.get('iso_install_mode', False)
- self.enable_eip_man = config.get('enable_eip_man', False)
- self.offline_deploy = config.get('offline_deploy', False) or os.environ.get('OFFLINE_DEPLOY') == 'true'
- self.enable_lbagent = config.get('enable_lbagent', False)
- self.enable_containerd = config.get(KEY_ENABLE_CONTAINERD, False)
- # AI 环境开关:仅在 product_version 为 AI 且显式开启时触发 utils/ai-env role
- self.enable_ai_env = config.get('enable_ai_env', False)
- self.host_networks = config.get(KEY_HOST_NETWORKS, None)
- if isinstance(self.host_networks, str):
- self.host_networks = [self.host_networks]
- self.disk_paths = config.get(KEY_DISK_PATHS, None)
- self.primary_master_node_ip = config.get(KEY_PRIMARY_MASTER_NODE_IP, None)
- def ansible_vars(self):
- vars = {
- 'docker_registry_mirrors': self.registry_mirrors,
- 'docker_insecure_registries': self.insecure_registries,
- KEY_K8S_CONTROLPLANE_HOST: self.controlplane_host,
- KEY_K3S_API_ENDPOINT: self.controlplane_host,
- 'k8s_controlplane_port': self.controlplane_port,
- KEY_K3S_API_PORT: self.controlplane_port,
- 'k8s_node_as_oc_host': self.as_host,
- 'k8s_node_as_oc_host_on_vm': self.as_host_on_vm,
- 'enable_eip_man': self.enable_eip_man,
- 'offline_deploy': self.offline_deploy,
- 'enable_lbagent': self.enable_lbagent,
- KEY_ENABLE_CONTAINERD: self.enable_containerd,
- 'enable_ai_env': self.enable_ai_env,
- }
- if self.high_availability_vip:
- vars['high_availability_vip'] = self.high_availability_vip
- vars['keepalived_version_tag'] = self.keepalived_version_tag
- vars['keepalived_password'] = self.keepalived_password
- vars['keepalived_router_id'] = self.keepalived_router_id
- if self.onecloud_version:
- vars[KEY_ONECLOUD_VERSION] = self.onecloud_version
- if self.node_ip:
- vars['node_ip'] = self.node_ip
- if self.iso_install_mode:
- vars['iso_install_mode'] = True
- if self.host_networks:
- vars[KEY_HOST_NETWORKS] = self.host_networks
- if self.disk_paths:
- vars[KEY_DISK_PATHS] = self.disk_paths
- if self.primary_master_node_ip:
- vars[KEY_PRIMARY_MASTER_NODE_IP] = self.primary_master_node_ip
- return vars
- class PrimaryMasterConfig(OnecloudConfig):
- # All kinds of products
- PRODUCT_VERSION_FULL_STACK = "FullStack"
- # Cloud Management Platform product
- PRODUCT_VERSION_CMP = "CMP"
- # Private Cloud Edge on-premise product
- PRODUCT_VERSION_EDGE = "Edge"
- PRODUCT_VERSION_LIGHT_EDGE = "LightEdge"
- # AI Cloud product
- PRODUCT_VERSION_AI = "AI"
- PRODUCT_VERSIONS = [
- PRODUCT_VERSION_FULL_STACK,
- PRODUCT_VERSION_CMP,
- PRODUCT_VERSION_EDGE,
- PRODUCT_VERSION_LIGHT_EDGE,
- PRODUCT_VERSION_AI,
- ]
- def __init__(self, config, bastion_host=None):
- super(PrimaryMasterConfig, self).__init__(config)
- self.node = Node(config).with_bastion(bastion_host)
- self.db_user = config.get('db_user', 'root')
- self.db_host = config.ensure_get('db_host')
- self.db_port = config.get('db_port', 3306)
- self.db_password = config.ensure_get('db_password')
- self.onecloud_version = config.ensure_get(KEY_ONECLOUD_VERSION)
- self.operator_version = config.get(KEY_OPERATOR_VERSION, self.onecloud_version)
- self.restore_mode = config.get('restore_mode', False)
- # 优先使用配置文件中设置的ip_type,如果没有设置则自动检测
- config_ip_type = config.get('ip_type', None)
- print(f"PrimaryMasterConfig config_ip_type is {config_ip_type}")
- if config_ip_type:
- # 使用配置文件中设置的ip_type
- self.ip_type = config_ip_type
- else:
- # 自动检测IP类型
- match_ip, ip_type = match_ipaddr(self.node.node_ip)
- self.ip_type = ip_type if match_ip else consts.IP_TYPE_IPV4
- # 支持双栈配置
- if self.ip_type == consts.IP_TYPE_DUAL_STACK:
- # 双栈配置:需要获取IPv4和IPv6地址
- self.node_ip_v4 = config.get('node_ip_v4', None)
- self.node_ip_v6 = config.get('node_ip_v6', None)
- self.pod_network_cidr_v4 = config.get('pod_network_cidr_v4', '10.40.0.0/16')
- self.service_cidr_v4 = config.get('service_cidr_v4', '10.96.0.0/12')
- # IPIP配置选项
- self.enable_ipip = config.get('enable_ipip', False)
- # set calico ip_autodetection_method only in primary master
- self.ip_autodetection_method = config.get('ip_autodetection_method', None)
- if not self.ip_autodetection_method:
- if self.ip_type == consts.IP_TYPE_IPV6:
- # For IPv6, use interface-based detection to avoid syntax issues
- self.ip_autodetection_method = "'first-found'"
- elif self.ip_type == consts.IP_TYPE_DUAL_STACK:
- # For dual-stack, use can-reach with primary IP to ensure correct interface selection
- self.ip_autodetection_method = "'can-reach=%s'" % self.node.node_ip
- else:
- self.ip_autodetection_method = "'can-reach=%s'" % self.node.node_ip
- self.onecloud_user = config.get('onecloud_user', 'admin')
- self.onecloud_user_password = config.get('onecloud_user_password', 'admin@123')
- self.use_ee = config.get('use_ee', False)
- self.image_repository = config.get('image_repository', consts.REGISTRY_ALI_YUNION)
- if utils.is_below_v3_9(self.onecloud_version):
- self.image_repository = consts.REGISTRY_ALI_YUNIONIO
- self.enable_minio = config.get('enable_minio', False)
- self.offline_nodes = config.get('offline_nodes', '')
- # Set default network CIDRs based on IP type
- if self.ip_type == consts.IP_TYPE_IPV6:
- # IPv6 default networks - k3s treats IPv6+IPv4 dual-stack as IPv4
- # For pure IPv6, cluster-cidr must be smaller than node-cidr-mask-size (/64)
- # Use /56 for cluster to allow multiple /64 node subnets
- default_pod_cidr = config.get('pod_network_cidr', 'fd85:ee78:d8a6:8607::/56')
- default_service_cidr = config.get('service_cidr', 'fd85:ee78:d8a6:8608::/112')
- elif self.ip_type == consts.IP_TYPE_DUAL_STACK:
- # Dual-stack networks
- default_pod_cidr = config.get('pod_network_cidr', 'fd85:ee78:d8a6:8607::/56')
- default_service_cidr = config.get('service_cidr', 'fd85:ee78:d8a6:8608::/112')
- else:
- # IPv4 default networks (also used for dual-stack as per k3s behavior)
- default_pod_cidr = config.get('pod_network_cidr', '10.40.0.0/16')
- default_service_cidr = config.get('service_cidr', '10.96.0.0/12')
- self.pod_network_cidr = default_pod_cidr
- self.service_cidr = default_service_cidr
- self.service_dns_domain = config.get('service_dns_domain', 'cluster.local')
- self.product_version = self.get_product_version(config)
- self.user_dns = config.get(KEY_USER_DNS, [])
- self.region = config.get(KEY_REGION, "region0")
- self.zone = config.get(KEY_ZONE, "zone0")
- def get_product_version(self, config):
- pv = config.get('product_version', self.PRODUCT_VERSION_FULL_STACK)
- if pv in self.PRODUCT_VERSIONS:
- return pv
- raise Exception("Unsupported product_version: %s" % pv)
- @classmethod
- def get_group(cls):
- return GROUP_PRIMARY_MASTER_NODE
- def ansible_vars(self):
- vars = super(PrimaryMasterConfig, self).ansible_vars()
- vars['db_host'] = self.db_host
- vars['db_port'] = self.db_port
- vars['db_user'] = self.db_user
- vars['db_password'] = self.db_password
- vars[KEY_ONECLOUD_VERSION] = self.onecloud_version
- vars[KEY_OPERATOR_VERSION] = self.operator_version
- vars['onecloud_user'] = self.onecloud_user
- vars['onecloud_user_password'] = self.onecloud_user_password
- vars['use_ee'] = self.use_ee
- vars['apiserver_advertise_address'] = self.node.node_ip
- vars[KEY_K3S_API_ENDPOINT] = self.controlplane_host
- vars['ip_autodetection_method'] = self.ip_autodetection_method
- vars['image_repository'] = self.image_repository
- vars['enable_minio'] = self.enable_minio
- vars['restore_mode'] = self.restore_mode
- vars['pod_network_cidr'] = self.pod_network_cidr
- vars['service_cidr'] = self.service_cidr
- vars['service_dns_domain'] = self.service_dns_domain
- vars['ip_type'] = self.ip_type
- # 添加双栈配置变量
- if self.ip_type == consts.IP_TYPE_DUAL_STACK:
- vars['node_ip_v4'] = self.node_ip_v4
- vars['node_ip_v6'] = self.node_ip_v6
- vars['pod_network_cidr_v4'] = self.pod_network_cidr_v4
- vars['service_cidr_v4'] = self.service_cidr_v4
- vars['enable_ipip'] = self.enable_ipip
- if len(self.offline_nodes) > 0:
- vars['offline_nodes'] = ' '.join(self.offline_nodes)
- vars['product_version'] = self.product_version
- if self.user_dns:
- vars[KEY_USER_DNS] = self.user_dns
- vars[KEY_REGION] = self.region
- vars[KEY_ZONE] = self.zone
- return vars
- def get_nodes(self):
- return [self.node]
- class OnecloudJointConfig(OnecloudConfig):
- def __init__(self, config):
- super(OnecloudJointConfig, self).__init__(config)
- self.as_controller = config.get('as_controller', None)
- self.join_token = config.get('join_token', None)
- self.join_cert_key = config.get('join_certificate_key', None)
- self.ntpd_server = config.get('ntpd_server', None)
- self.controlplane_ssh_port = config.get('controlplane_ssh_port', 22)
- def ansible_vars(self):
- vars = super(OnecloudJointConfig, self).ansible_vars()
- vars['k8s_node_as_oc_controller'] = self.as_controller
- vars['k8s_controlplane_ssh_port'] = self.controlplane_ssh_port
- if self.join_token:
- vars['k8s_join_token'] = self.join_token
- if self.join_cert_key:
- vars['k8s_join_certificate_key'] = self.join_cert_key
- # TODO: ntpd_server should define in all nodes config?
- if self.ntpd_server:
- vars['ntpd_server'] = self.ntpd_server
- return vars
- def get_nodes(config, bastion_host=None):
- host_configs = config.ensure_get('hosts')
- nodes = []
- for host_config in host_configs:
- node = Node(Config(host_config)).with_bastion(bastion_host)
- nodes.append(node)
- return nodes
- class MasterConfig(OnecloudJointConfig):
- def __init__(self, config, bastion_host=None):
- super(MasterConfig, self).__init__(config)
- if self.as_controller is None:
- self.as_controller = True
- self.nodes = get_nodes(config, bastion_host)
- self.primary_master_config = None
- def set_primary_master_config(self, primary_master_config):
- self.primary_master_config = primary_master_config
- def ansible_vars(self):
- vars = super(MasterConfig, self).ansible_vars()
- pc = self.primary_master_config
- vars['pod_network_cidr'] = pc.pod_network_cidr
- vars['service_cidr'] = pc.service_cidr
- vars['service_dns_domain'] = pc.service_dns_domain
- vars['image_repository'] = pc.image_repository
- vars['ip_type'] = pc.ip_type
- # 添加双栈配置变量
- if pc.ip_type == consts.IP_TYPE_DUAL_STACK:
- vars['node_ip_v4'] = pc.node_ip_v4
- vars['node_ip_v6'] = pc.node_ip_v6
- vars['pod_network_cidr_v4'] = pc.pod_network_cidr_v4
- vars['service_cidr_v4'] = pc.service_cidr_v4
- vars['enable_ipip'] = pc.enable_ipip
- return vars
- @classmethod
- def get_group(cls):
- return GROUP_MASTER_NODES
- def get_nodes(self):
- return self.nodes
- class WorkerConfig(OnecloudJointConfig):
- def __init__(self, config, bastion_host=None):
- super(WorkerConfig, self).__init__(config)
- if self.as_host is None:
- self.as_host = True
- if self.as_host_on_vm is None:
- self.as_host_on_vm = False
- self.nodes = get_nodes(config, bastion_host)
- self.primary_master_config = None
- def set_primary_master_config(self, primary_master_config):
- self.primary_master_config = primary_master_config
- def ansible_vars(self):
- vars = super(WorkerConfig, self).ansible_vars()
- # Worker-specific: k3s agent token
- vars[KEY_K3S_TOKEN] = VAL_K3S_TOKEN
- if self.primary_master_config:
- # Use exactly the same logic as MasterConfig
- pc = self.primary_master_config
- print("WorkerConfig primary_master_config", pc)
- for attr in dir(pc):
- if not attr.startswith("__") and not callable(getattr(pc, attr)):
- print(f"pc.{attr} = {getattr(pc, attr)}")
- vars['pod_network_cidr'] = pc.pod_network_cidr
- vars['service_cidr'] = pc.service_cidr
- vars['service_dns_domain'] = pc.service_dns_domain
- vars['image_repository'] = pc.image_repository
- vars['ip_type'] = pc.ip_type
- # Worker-specific: needed for Calico IP detection on worker nodes
- vars['ip_autodetection_method'] = pc.ip_autodetection_method
- # 添加双栈配置变量
- if pc.ip_type == consts.IP_TYPE_DUAL_STACK:
- vars['node_ip_v4'] = pc.node_ip_v4
- vars['node_ip_v6'] = pc.node_ip_v6
- vars['pod_network_cidr_v4'] = pc.pod_network_cidr_v4
- vars['service_cidr_v4'] = pc.service_cidr_v4
- vars['enable_ipip'] = pc.enable_ipip
- # Critical: API endpoint variables for kubernetes-services-endpoint ConfigMap
- vars[KEY_K3S_API_ENDPOINT] = self.controlplane_host
- vars[KEY_K3S_API_PORT] = self.controlplane_port
- return vars
- @classmethod
- def get_group(cls):
- return GROUP_WORKER_NODES
- def get_nodes(self):
- return self.nodes
- class NodeConfig(object):
- def __init__(self, config, bastion_host=None):
- self.nodes = get_nodes(config, bastion_host)
- @classmethod
- def get_group(cls):
- return GROUP_NODES
- def get_nodes(self):
- return self.nodes
- def ansible_vars(self):
- return {}
|