copied from internal repo

This commit is contained in:
Dragroth
2026-01-22 12:37:26 +01:00
parent a475364582
commit 7451b63f62
118 changed files with 17182 additions and 0 deletions

0
lib/__init__.py Normal file
View File

292
lib/create_sip_trunk.py Normal file
View File

@@ -0,0 +1,292 @@
import argparse
import configparser
import os
import pathlib
import re
import sys
from itertools import product
from conpeek_setup import util
from conpeek_setup.api import operator_api
def unpack_regex(regex):
# Helper function to expand ranges and lists within square brackets, e.g., [2-5] or [234]
def expand_square_brackets(pattern):
expanded = []
content = pattern[1:-1] # Remove the square brackets
if '-' in content:
# Handle ranges like [2-5]
start, end = content.split('-')
expanded = [str(i) for i in range(int(start), int(end) + 1)]
elif pattern == '\\d':
return [str(i) for i in range(10)]
else:
# Handle specific digits like [234]
expanded = list(content)
return expanded
# Split the regex into parts of digits and square bracket patterns
# This regex splits on digits and square brackets with their content
parts = re.findall(r'\[\d+-\d+\]|\[\d+\]|\d+|\\d', regex)
# Process parts to generate all possible values
expanded_parts = []
for part in parts:
if part.startswith('[') and part.endswith(']'):
# Handle square brackets
expanded_parts.append(expand_square_brackets(part))
elif part == '\d':
expanded_parts.append(expand_square_brackets(part))
else:
# Handle digits
expanded_parts.append([part])
for combination in product(*expanded_parts):
print(''.join(combination))
# Generate all combinations of expanded parts
possible_values = [''.join(combination) for combination in product(*expanded_parts)]
print(possible_values)
return possible_values
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="")
parser.add_argument("--config", type=str, required=True, help="Path to the config file")
parser.add_argument("--user", type=str, required=True, help="Operator login")
parser.add_argument("--password", type=str, required=True, help="Operator password")
parser.add_argument("--output-path", type=str, required=False, help="Path to the output")
args = parser.parse_args()
config = configparser.ConfigParser()
config_directory = args.config
if not os.path.abspath(config_directory):
config_directory = os.path.join(pathlib.Path(__file__).parent.resolve(), config_directory)
config.read(config_directory)
if args.output_path:
util.Settings.output_directory = args.output_path
output_directory = os.path.join(util.get_output_path(), "freeswitch")
template_freeswitch_directory = os.path.join(util.get_templates_path(), "freeswitch/conf")
app_domain = config["new_machine_network"]["app_installation_domain"]
api = operator_api.OperatorAPI(app_domain)
api.login(args.user, args.password)
tenant = None
tenants = api.get_tenants()
for o_tenant in tenants:
tenant = o_tenant
break
if not tenant:
util.print_red("Tenant not exists. First you need to create tenant and the run this script.")
sys.exit()
# sip trunks
for section in config:
if not section.startswith("sip_trunk_"):
continue
operator = "default"
if config.has_option(section, "operator") and config[section]["operator"]:
if config[section]["operator"] not in util.Constants.sip_trunk_operators:
util.print_red(f'Operator does not exists {config[section]["operator"]}. Using default sip trunk!')
else:
operator = config[section]["operator"]
siptrunk_number = section[10:]
proxy = config[section]["proxy"]
SIP_TRUNK_NUMBER = str(int(siptrunk_number) + 1)
SIP_TRUNK_INTERNAL_PORT = 5100 + int(siptrunk_number)
SIP_TRUNK_NAME = f"ic{siptrunk_number}"
SIP_TRUNK_NAME_WITH_PORT = f"{SIP_TRUNK_NAME}_{SIP_TRUNK_INTERNAL_PORT}"
SIP_PROFILE_NAME = f"gw1_external_{siptrunk_number}_{SIP_TRUNK_INTERNAL_PORT}"
template_freeswitch_directory_operator = os.path.join(util.get_templates_path(), f"freeswitch/{operator}")
sip_profile_template_path = os.path.join(template_freeswitch_directory_operator, "sip_profiles/gw_external.xml")
if not os.path.exists(sip_profile_template_path):
sip_profile_template_path = os.path.join(template_freeswitch_directory, "sip_profiles/gw_external.xml")
sip_profile_output_path = os.path.join(output_directory, f"conf/sip_profiles/{SIP_PROFILE_NAME}.xml")
if os.path.exists(sip_profile_output_path):
util.print_red(f"SIP profile {sip_profile_output_path} exists.")
continue
else:
util.copy_file(sip_profile_template_path, sip_profile_output_path)
SIP_TRUNK_USERNAME = ""
if config.has_option(section, "username"):
SIP_TRUNK_USERNAME = config[section]["username"]
SIP_TRUNK_PASSWORD = ""
if config.has_option(section, "password"):
SIP_TRUNK_PASSWORD = config[section]["password"]
sip_profile_replacements = {
'SIP_PROFILE_NAME': SIP_PROFILE_NAME,
'SIP_TRUNK_NAME_WITH_PORT': SIP_TRUNK_NAME_WITH_PORT,
'SIP_TRUNK_PROXY': proxy,
'SIP_TRUNK_INTERNAL_PORT': str(SIP_TRUNK_INTERNAL_PORT),
'SIP_TRUNK_USERNAME': SIP_TRUNK_USERNAME,
'SIP_TRUNK_PASSWORD': SIP_TRUNK_PASSWORD,
'EXTERNAL_IP': config["new_machine_network"]["external_ip"],
'INTERNAL_IP': config["new_machine_network"]["internal_ip"],
}
for name in sip_profile_replacements.keys():
util.basic_on_location_sed(sip_profile_output_path, name, sip_profile_replacements[name])
operator_gateways = api.get_operator_gateways()
operator_gateway = None
for o_gateway in operator_gateways:
if o_gateway["name"] == SIP_TRUNK_NAME:
operator_gateway = o_gateway
break
tenant_telco_networks = api.get_tenant_telco_networks(tenant["id"])
tenant_has_telco_network_exists = False
for tenant_has_telco_network in tenant_telco_networks:
if tenant_has_telco_network["gateway_external_sip_port"] == SIP_TRUNK_INTERNAL_PORT:
tenant_has_telco_network_exists = True
break
if not tenant_has_telco_network_exists:
api.create_tenant_telco_network(tenant["id"], {
"8021q_vlan_id": 0,
"gateway_external_sip_port": SIP_TRUNK_INTERNAL_PORT,
"telco_network_id": 1
})
if not operator_gateway:
util.print_black_light(f"Creating operator gateway {SIP_TRUNK_NAME}")
operator_gateway = api.create_operator_gateway({
"name": SIP_TRUNK_NAME,
"validate_e164": True,
"presentation_preservation_policy": "DISABLED",
"telco_network_id": 1,
"username": SIP_TRUNK_USERNAME,
"password": SIP_TRUNK_PASSWORD
})
if not operator_gateway:
util.print_red(f"Cant insert to operator_gateway {SIP_TRUNK_NAME}.")
continue
normalization_to_create = [{
"direction": "outbound",
"peer": "caller",
"pattern": '^48([0-9]{9})$',
"replacement": '\\1',
"priority": 10
}, {
"direction": "inbound",
"peer": "callee",
"pattern": '^([0-9]{9})$',
"replacement": '48\\1',
"priority": 10
}, {
"direction": "inbound",
"peer": "caller",
"pattern": '^([0-9]{9})$',
"replacement": '48\\1',
"priority": 10
}, {
"direction": "inbound",
"peer": "caller",
"pattern": '^\\+([0-9]*)$',
"replacement": '\\1',
"priority": 10
}, {
"direction": "inbound",
"peer": "caller",
"pattern": '^00([0-9]*)$',
"replacement": '\\1',
"priority": 10
}, {
"direction": "outbound",
"peer": "callee",
"pattern": '^([0-9]*)$',
"replacement": '+\\1',
"priority": 10
}]
for normalization in normalization_to_create:
api.create_operator_gateway_normalization(operator_gateway["id"], normalization)
ddi = config[section]["ddi"]
ddi = ddi.replace(",", ";")
ddi_patterns = ddi.split(";")
for ddi_pattern in ddi_patterns:
if ddi_pattern.startswith("^") and ddi_pattern.endswith("$"):
api.create_operator_routing({
"priority": 100,
"src_number_to_charge": ddi_pattern,
"src_presentation_number": ".*",
"dst": ".*",
"operator_gateway_id": operator_gateway["id"]
})
ddi_list = unpack_regex(ddi_pattern)
if len(ddi_list) > 1000:
util.print_red("There cannot be more than 1000 numbers -- invalid regexp pattern")
sys.exit()
for ddi_number in ddi_list:
api.create_ddi(tenant["id"], {
"e164number": ddi_number,
"fax_enabled": 1,
"voice_enabled": 1,
"sms_enabled": 0,
"operator_gateway_id": operator_gateway["id"]
})
elif "-" in ddi_pattern:
from_number = int(ddi_pattern.split("-")[0])
to_number = int(ddi_pattern.split("-")[1])
numbers = set(range(from_number, to_number + 1))
if len(numbers) > 1000:
util.print_red("There cannot be more than 1000 numbers")
sys.exit()
for number in numbers:
api.create_operator_routing({
"priority": 100,
"src_number_to_charge": f"^{number}$",
"src_presentation_number": ".*",
"dst": ".*",
"operator_gateway_id": operator_gateway["id"]
})
api.create_ddi(tenant["id"], {
"e164number": number,
"fax_enabled": 1,
"voice_enabled": 1,
"sms_enabled": 0,
"operator_gateway_id": operator_gateway["id"]
})
else:
api.create_operator_routing({
"priority": 100,
"src_number_to_charge": f"^{ddi_pattern}$",
"src_presentation_number": ".*",
"dst": ".*",
"operator_gateway_id": operator_gateway["id"]
})
api.create_ddi(tenant["id"], {
"e164number": ddi_pattern,
"fax_enabled": 1,
"voice_enabled": 1,
"sms_enabled": 0,
"operator_gateway_id": operator_gateway["id"]
})

228
lib/create_tenant.py Normal file
View File

@@ -0,0 +1,228 @@
import argparse
import configparser
import os
import pathlib
import sys
from datetime import datetime
import pytz
from conpeek_setup import util
from conpeek_setup.api import operator_api
from conpeek_setup.api.tenant_api import TenantAPI
def validate_config(config):
sections_to_validate = [
"tenant"
]
for section in sections_to_validate:
if not config.has_section(section):
util.print_red(f"No {section} section in config")
raise Exception(f"No {section} section in config")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="")
parser.add_argument("--config", type=str, required=True, help="Path to the config file")
parser.add_argument("--user", type=str, required=True, help="Operator login")
parser.add_argument("--password", type=str, required=True, help="Operator password")
args = parser.parse_args()
config = configparser.ConfigParser()
config_directory = args.config
if not os.path.abspath(config_directory):
config_directory = os.path.join(pathlib.Path(__file__).parent.resolve(), config_directory)
config.read(config_directory)
validate_config(config)
app_domain = config["new_machine_network"]["app_installation_domain"]
api = operator_api.OperatorAPI(app_domain)
util.print_black_light(f"Wait for start TenantLoginAPI")
api.wait_for_start(f"{api.api_tenant_login_url}/tenant/check_connection")
util.print_green(f"TenantLoginAPI Started")
api.login(args.user, args.password)
util.print_black_light(f"Wait for start OperatorAPI")
api.wait_for_start()
util.print_green(f"OperatorAPI Started")
tenant = None
tenants = api.get_tenants()
for o_tenant in tenants:
tenant = o_tenant
break
if not tenant:
# Create Tenant if not exists
print("Creating tenant...")
tenant = api.create_tenant({
"name": config["tenant"]["domain"],
"domain": config["tenant"]["domain"],
"time_zone": "Europe/Warsaw",
"language": "pl",
"active": 1,
"estimated_number_of_users": 100,
"allow_to_exceed_concurrent_packages": 0,
"create_recording_checksum_file": 1,
"video_recording_enabled": 0,
"simultaneous_calls_limit": 100,
"anonymous_sessions": 1,
"access_network_id": 1,
"plugin_enabled_domain_id": 1,
"telco_network_id": 1
})
util.print_black_light(f"Created tenant ({tenant['id']}) {tenant['name']}.")
else:
util.print_yellow(f"Tenant {config['tenant']['domain']} exists, id={tenant['id']}.")
license_packages = api.get_license_packages(tenant_id=tenant["id"])
packages_count = {
"Super Admin": 3,
}
license_package_agreements = api.get_package_agreements(tenant_id=tenant["id"])
for license_package in license_package_agreements:
if license_package["operator_license_package_name"] in packages_count:
if license_package["commercial_license_package_amount"] >= packages_count[license_package["operator_license_package_name"]]:
util.print_black_light(f'Licenses {license_package["operator_license_package_name"]} exists.')
del packages_count[license_package["operator_license_package_name"]]
super_admin_package_id = None
package_activation_item_list = []
for package_license in license_packages:
if package_license["name"] == "Super Admin":
super_admin_package_id = package_license["id"]
if package_license["name"] in packages_count:
package_activation_item_list.append({
"license_package_amount": packages_count[package_license["name"]],
"license_package_type": "COMMERCIAL",
"concurrent_usage_limit": 0,
"operator_license_package_id": package_license["id"]
})
local_tz = pytz.timezone(tenant["time_zone"])
local_now = datetime.now(local_tz)
local_start_of_day = local_tz.localize(datetime(local_now.year, local_now.month, local_now.day, 0, 0, 0))
utc_start_of_day = local_start_of_day.astimezone(pytz.utc)
if package_activation_item_list:
api.create_package_activation_request(tenant_id=tenant["id"], params={
"activate_date": utc_start_of_day.strftime("%Y-%m-%d %H:%M:%S"),
"note": "Auto generated",
"package_activation_item_list": package_activation_item_list
})
# Create super administrators
util.print_black_light("Creating super administrators...")
super_admin_users_to_create = [
{
"username": "super1",
"extension": "1001"
},
{
"username": "super2",
"extension": "1002"
},
{
"username": "super3",
"extension": "1003"
}
]
super_admin_users_to_create_list = []
for user in super_admin_users_to_create:
super_admin_users_to_create_list.append(user["username"])
users_in_tenant = []
super_tenant_user_id = None
tenant_api = None
user_mapping = {}
users = api.get_users(tenant_id=tenant["id"])
for user in users:
if not super_tenant_user_id:
super_tenant_user_id = user["id"]
user_mapping[user["username"]] = user["id"]
if not tenant_api:
login_data = api.login_as_user(tenant_id=tenant["id"], tenant_user_id=super_tenant_user_id)
tenant_api = TenantAPI(app_domain)
tenant_api.initialize_user_login(login_data)
util.print_black_light(f"Wait for start TenantAPI")
tenant_api.wait_for_start()
util.print_green(f"TenantAPI Started")
users_in_tenant.append(user["username"])
if super_admin_package_id:
for user in super_admin_users_to_create:
if user["username"] in users_in_tenant:
util.print_black_light(f'User {user["username"]} exists.')
continue
d_user = api.create_user(tenant_id=tenant["id"], params={
"active": 1,
"username": user["username"],
"password": args.password,
"main_license_package_id": super_admin_package_id,
"language": "pl",
"change_avatar_allowed": 1,
"change_on_leave_allowed": 1,
"on_leave": 0,
"system_role": [],
"tenant_role": []
})
user_mapping[user["username"]] = d_user["id"]
if not super_tenant_user_id:
super_tenant_user_id = d_user["id"]
if not tenant_api:
login_data = api.login_as_user(tenant_id=tenant["id"], tenant_user_id=super_tenant_user_id)
tenant_api = TenantAPI(app_domain)
tenant_api.initialize_user_login(login_data)
util.print_black_light(f"Wait for start TenantAPI")
tenant_api.wait_for_start()
util.print_green(f"TenantAPI Started")
tenant_api.update_feature_tenant_user_call(d_user["id"], {
"call_enabled": 1,
"extension": user["extension"],
"click_to_dial_auto_answer": 1
})
tenant_api.update_feature_tenant_user_consultant(d_user["id"], {
"work_state": "ONLINE"
})
tenant_api.update_feature_tenant_user_consultant(d_user["id"], {
"audio_enabled": 1,
"file_enabled": 1,
"chat_enabled": 1
})
tenant_api.update_feature_tenant_user_hotdesk(d_user["id"], {
"hotdesk_enabled": 1,
"hotdesk_identifier": user["extension"],
"hotdesk_pin": user["extension"],
})
tenant_api.create_web_link({
"name": "Web",
"tenant_user_id": d_user["id"],
"audio": 1,
"chat": 1,
"video": 1,
"call_timeout": 30,
"priority": 100,
"state": "ON",
"user_device_type": "WEBAPP"
})
if not tenant_api:
sys.exit()