Add first version of set domain provider

This commit is contained in:
MercierCorentin 2021-05-20 13:29:04 +02:00
parent bb140b2ba4
commit 51d6d19810
5 changed files with 344 additions and 161 deletions

View file

@ -582,6 +582,30 @@ domain:
full: --delete full: --delete
help: Delete the key help: Delete the key
action: store_true action: store_true
subcategories:
registrar:
subcategory_help: Manage domains registrars
actions:
### domain_registrar_set()
set:
action_help: Set domain registrar
api: POST /domains/registrar
arguments:
domain:
help: Domain name
registrar:
help: registrar_key, see yunohost domain registrar list
-a:
full: --args
help: Serialized arguments for app script (i.e. "domain=domain.tld&path=/path")
### domain_registrar_set()
get:
action_help: Get domain registrar
api: GET /domains/registrar
### domain_registrar_list()
list:
action_help: List available registrars
api: GET /domains/registrar/list
############################# #############################
# App # # App #

View file

@ -1,3 +1,114 @@
# This file is automatically generated #
# during Debian's package build by the script # completion for yunohost
# data/actionsmap/yunohost_completion.py # automatically generated from the actionsmap
#
_yunohost()
{
local cur prev opts narg
COMPREPLY=()
# the number of words already typed
narg=${#COMP_WORDS[@]}
# the current word being typed
cur="${COMP_WORDS[COMP_CWORD]}"
# If one is currently typing a category,
# match with categorys
if [[ $narg == 2 ]]; then
opts="user domain app backup settings service firewall dyndns tools hook log diagnosis"
fi
# If one already typed a category,
# match the actions or the subcategories of that category
if [[ $narg == 3 ]]; then
# the category typed
category="${COMP_WORDS[1]}"
if [[ $category == "user" ]]; then
opts="list create delete update info group permission ssh"
fi
if [[ $category == "domain" ]]; then
opts="list add registrar push_config remove dns-conf main-domain cert-status cert-install cert-renew url-available setting "
fi
if [[ $category == "app" ]]; then
opts="catalog search manifest fetchlist list info map install remove upgrade change-url setting register-url makedefault ssowatconf change-label addaccess removeaccess clearaccess action config"
fi
if [[ $category == "backup" ]]; then
opts="create restore list info download delete "
fi
if [[ $category == "settings" ]]; then
opts="list get set reset-all reset "
fi
if [[ $category == "service" ]]; then
opts="add remove start stop reload restart reload_or_restart enable disable status log regen-conf "
fi
if [[ $category == "firewall" ]]; then
opts="list allow disallow upnp reload stop "
fi
if [[ $category == "dyndns" ]]; then
opts="subscribe update installcron removecron "
fi
if [[ $category == "tools" ]]; then
opts="adminpw maindomain postinstall update upgrade shell shutdown reboot regen-conf versions migrations"
fi
if [[ $category == "hook" ]]; then
opts="add remove info list callback exec "
fi
if [[ $category == "log" ]]; then
opts="list show share "
fi
if [[ $category == "diagnosis" ]]; then
opts="list show get run ignore unignore "
fi
fi
# If one already typed an action or a subcategory,
# match the actions of that subcategory
if [[ $narg == 4 ]]; then
# the category typed
category="${COMP_WORDS[1]}"
# the action or the subcategory typed
action_or_subcategory="${COMP_WORDS[2]}"
if [[ $category == "user" ]]; then
if [[ $action_or_subcategory == "group" ]]; then
opts="list create delete info add remove"
fi
if [[ $action_or_subcategory == "permission" ]]; then
opts="list info update add remove reset"
fi
if [[ $action_or_subcategory == "ssh" ]]; then
opts="list-keys add-key remove-key"
fi
fi
if [[ $category == "app" ]]; then
if [[ $action_or_subcategory == "action" ]]; then
opts="list run"
fi
if [[ $action_or_subcategory == "config" ]]; then
opts="show-panel apply"
fi
fi
if [[ $category == "tools" ]]; then
if [[ $action_or_subcategory == "migrations" ]]; then
opts="list run state"
fi
fi
fi
# If no options were found propose --help
if [ -z "$opts" ]; then
prev="${COMP_WORDS[COMP_CWORD-1]}"
if [[ $prev != "--help" ]]; then
opts=( --help )
fi
fi
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
}
complete -F _yunohost yunohost

View file

@ -1,218 +1,218 @@
- aliyun aliyun:
- auth_key_id - auth_key_id
- auth_secret - auth_secret
- aurora aurora:
- auth_api_key - auth_api_key
- auth_secret_key - auth_secret_key
- azure azure:
- auth_client_id - auth_client_id
- auth_client_secret - auth_client_secret
- auth_tenant_id - auth_tenant_id
- auth_subscription_id - auth_subscription_id
- resource_group - resource_group
- cloudflare cloudflare:
- auth_username - auth_username
- auth_token - auth_token
- zone_id - zone_id
- cloudns cloudns:
- auth_id - auth_id
- auth_subid - auth_subid
- auth_subuser - auth_subuser
- auth_password - auth_password
- weight - weight
- port - port
- cloudxns cloudxns:
- auth_username - auth_username
- auth_token - auth_token
- conoha conoha:
- auth_region - auth_region
- auth_token - auth_token
- auth_username - auth_username
- auth_password - auth_password
- auth_tenant_id - auth_tenant_id
- constellix constellix:
- auth_username - auth_username
- auth_token - auth_token
- digitalocean digitalocean:
- auth_token - auth_token
- dinahosting dinahosting:
- auth_username - auth_username
- auth_password - auth_password
- directadmin directadmin:
- auth_password - auth_password
- auth_username - auth_username
- endpoint - endpoint
- dnsimple dnsimple:
- auth_token - auth_token
- auth_username - auth_username
- auth_password - auth_password
- auth_2fa - auth_2fa
- dnsmadeeasy dnsmadeeasy:
- auth_username - auth_username
- auth_token - auth_token
- dnspark dnspark:
- auth_username - auth_username
- auth_token - auth_token
- dnspod dnspod:
- auth_username - auth_username
- auth_token - auth_token
- dreamhost dreamhost:
- auth_token - auth_token
- dynu dynu:
- auth_token - auth_token
- easydns easydns:
- auth_username - auth_username
- auth_token - auth_token
- easyname easyname:
- auth_username - auth_username
- auth_password - auth_password
- euserv euserv:
- auth_username - auth_username
- auth_password - auth_password
- exoscale exoscale:
- auth_key - auth_key
- auth_secret - auth_secret
- gandi gandi:
- auth_token - auth_token
- api_protocol - api_protocol
- gehirn gehirn:
- auth_token - auth_token
- auth_secret - auth_secret
- glesys glesys:
- auth_username - auth_username
- auth_token - auth_token
- godaddy godaddy:
- auth_key - auth_key
- auth_secret - auth_secret
- googleclouddns googleclouddns:
- auth_service_account_info - auth_service_account_info
- gransy gransy:
- auth_username - auth_username
- auth_password - auth_password
- gratisdns gratisdns:
- auth_username - auth_username
- auth_password - auth_password
- henet henet:
- auth_username - auth_username
- auth_password - auth_password
- hetzner hetzner:
- auth_token - auth_token
- hostingde hostingde:
- auth_token - auth_token
- hover hover:
- auth_username - auth_username
- auth_password - auth_password
- infoblox infoblox:
- auth_user - auth_user
- auth_psw - auth_psw
- ib_view - ib_view
- ib_host - ib_host
- infomaniak infomaniak:
- auth_token - auth_token
- internetbs internetbs:
- auth_key - auth_key
- auth_password - auth_password
- inwx inwx:
- auth_username - auth_username
- auth_password - auth_password
- joker joker:
- auth_token - auth_token
- linode linode:
- auth_token - auth_token
- linode4 linode4:
- auth_token - auth_token
- localzone localzone:
- filename - filename
- luadns luadns:
- auth_username - auth_username
- auth_token - auth_token
- memset memset:
- auth_token - auth_token
- mythicbeasts mythicbeasts:
- auth_username - auth_username
- auth_password - auth_password
- auth_token - auth_token
- namecheap namecheap:
- auth_token - auth_token
- auth_username - auth_username
- auth_client_ip - auth_client_ip
- auth_sandbox - auth_sandbox
- namesilo namesilo:
- auth_token - auth_token
- netcup netcup:
- auth_customer_id - auth_customer_id
- auth_api_key - auth_api_key
- auth_api_password - auth_api_password
- nfsn nfsn:
- auth_username - auth_username
- auth_token - auth_token
- njalla njalla:
- auth_token - auth_token
- nsone nsone:
- auth_token - auth_token
- onapp onapp:
- auth_username - auth_username
- auth_token - auth_token
- auth_server - auth_server
- online online:
- auth_token - auth_token
- ovh ovh:
- auth_entrypoint - auth_entrypoint
- auth_application_key - auth_application_key
- auth_application_secret - auth_application_secret
- auth_consumer_key - auth_consumer_key
- plesk plesk:
- auth_username - auth_username
- auth_password - auth_password
- plesk_server - plesk_server
- pointhq pointhq:
- auth_username - auth_username
- auth_token - auth_token
- powerdns powerdns:
- auth_token - auth_token
- pdns_server - pdns_server
- pdns_server_id - pdns_server_id
- pdns_disable_notify - pdns_disable_notify
- rackspace rackspace:
- auth_account - auth_account
- auth_username - auth_username
- auth_api_key - auth_api_key
- auth_token - auth_token
- sleep_time - sleep_time
- rage4 rage4:
- auth_username - auth_username
- auth_token - auth_token
- rcodezero rcodezero:
- auth_token - auth_token
- route53 route53:
- auth_access_key - auth_access_key
- auth_access_secret - auth_access_secret
- private_zone - private_zone
- auth_username - auth_username
- auth_token - auth_token
- safedns safedns:
- auth_token - auth_token
- sakuracloud sakuracloud:
- auth_token - auth_token
- auth_secret - auth_secret
- softlayer softlayer:
- auth_username - auth_username
- auth_api_key - auth_api_key
- transip transip:
- auth_username - auth_username
- auth_api_key - auth_api_key
- ultradns ultradns:
- auth_token - auth_token
- auth_username - auth_username
- auth_password - auth_password
- vultr vultr:
- auth_token - auth_token
- yandex yandex:
- auth_token - auth_token
- zeit zeit:
- auth_token - auth_token
- zilore zilore:
- auth_key - auth_key
- zonomi zonomi:
- auth_token - auth_token
- auth_entrypoint - auth_entrypoint

View file

@ -2977,6 +2977,7 @@ ARGUMENTS_TYPE_PARSERS = {
} }
def _parse_args_in_yunohost_format(user_answers, argument_questions): def _parse_args_in_yunohost_format(user_answers, argument_questions):
"""Parse arguments store in either manifest.json or actions.json or from a """Parse arguments store in either manifest.json or actions.json or from a
config panel against the user answers when they are present. config panel against the user answers when they are present.

View file

@ -29,8 +29,8 @@ import sys
import yaml import yaml
import functools import functools
from lexicon.config import ConfigResolver # from lexicon.config import ConfigResolver
from lexicon.client import Client # from lexicon.client import Client
from moulinette import m18n, msettings, msignals from moulinette import m18n, msettings, msignals
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
@ -43,6 +43,7 @@ from yunohost.app import (
_installed_apps, _installed_apps,
_get_app_settings, _get_app_settings,
_get_conflicting_apps, _get_conflicting_apps,
_parse_args_in_yunohost_format
) )
from yunohost.regenconf import regen_conf, _force_clear_hashes, _process_regen_conf from yunohost.regenconf import regen_conf, _force_clear_hashes, _process_regen_conf
from yunohost.utils.network import get_public_ip from yunohost.utils.network import get_public_ip
@ -53,6 +54,7 @@ from yunohost.hook import hook_callback
logger = getActionLogger("yunohost.domain") logger = getActionLogger("yunohost.domain")
DOMAIN_SETTINGS_PATH = "/etc/yunohost/domains.yml" DOMAIN_SETTINGS_PATH = "/etc/yunohost/domains.yml"
REGISTRAR_LIST_PATH = "/usr/share/yunohost/other/providers_list.yml"
def domain_list(exclude_subdomains=False): def domain_list(exclude_subdomains=False):
""" """
@ -825,105 +827,150 @@ def _set_domain_settings(domain, domain_settings):
with open(DOMAIN_SETTINGS_PATH, 'w') as file: with open(DOMAIN_SETTINGS_PATH, 'w') as file:
yaml.dump(domains, file, default_flow_style=False) yaml.dump(domains, file, default_flow_style=False)
# def domain_get_registrar():
def domain_registrar_set(domain, registrar, args):
domains = _load_domain_settings()
if not domain in domains.keys():
raise YunohostError("domain_name_unknown", domain=domain)
def domain_push_config(domain): registrars = yaml.load(open(REGISTRAR_LIST_PATH, "r+"))
""" if not registrar in registrars.keys():
Send DNS records to the previously-configured registrar of the domain. # FIXME créer l'erreur
""" raise YunohostError("registrar_unknown")
# Generate the records
if domain not in domain_list()["domains"]: parameters = registrars[registrar]
raise YunohostValidationError("domain_name_unknown", domain=domain) ask_args = []
for parameter in parameters:
ask_args.append({
'name' : parameter,
'type': 'string',
'example': '',
'default': '',
})
args_dict = (
{} if not args else dict(urllib.parse.parse_qsl(args, keep_blank_values=True))
)
parsed_answer_dict = _parse_args_in_yunohost_format(args_dict, ask_args)
domains_settings = _get_domain_settings(domain, True) domain_provider = {
'name': registrar,
dns_conf = _build_dns_conf(domains_settings) 'options': {
# Flatten the DNS conf
flatten_dns_conf = []
for key in dns_conf:
list_of_records = dns_conf[key]
for record in list_of_records:
# FIXME Lexicon does not support CAA records
# See https://github.com/AnalogJ/lexicon/issues/282 and https://github.com/AnalogJ/lexicon/pull/371
# They say it's trivial to implement it!
# And yet, it is still not done/merged
if record["type"] != "CAA":
# Add .domain.tdl to the name entry
record["name"] = "{}.{}".format(record["name"], domain)
flatten_dns_conf.append(record)
# Get provider info
# TODO
provider = {
"name": "gandi",
"options": {
"api_protocol": "rest",
"auth_token": "vhcIALuRJKtoZiZyxfDYWLom"
} }
} }
for arg_name, arg_value_and_type in parsed_answer_dict.items():
domain_provider['options'][arg_name] = arg_value_and_type[0]
domain_settings = domains[domain]
domain_settings["provider"] = domain_provider
# Construct the base data structure to use lexicon's API. # Save the settings to the .yaml file
base_config = { with open(DOMAIN_SETTINGS_PATH, 'w') as file:
"provider_name": provider["name"], yaml.dump(domains, file, default_flow_style=False)
"domain": domain, # domain name
}
base_config[provider["name"]] = provider["options"]
# Get types present in the generated records
types = set()
for record in flatten_dns_conf:
types.add(record["type"])
# Fetch all types present in the generated records
distant_records = {}
for key in types: # def domain_push_config(domain):
record_config = { # """
"action": "list", # Send DNS records to the previously-configured registrar of the domain.
"type": key, # """
} # # Generate the records
final_lexicon = ConfigResolver().with_dict(dict_object=base_config).with_dict(dict_object=record_config) # if domain not in domain_list()["domains"]:
# print('final_lexicon:', final_lexicon); # raise YunohostValidationError("domain_name_unknown", domain=domain)
client = Client(final_lexicon)
distant_records[key] = client.execute()
for key in types: # domains_settings = _get_domain_settings(domain, True)
for distant_record in distant_records[key]:
print('distant_record:', distant_record);
for local_record in flatten_dns_conf:
print('local_record:', local_record);
# Push the records # dns_conf = _build_dns_conf(domains_settings)
for record in flatten_dns_conf:
# For each record, first check if one record exists for the same (type, name) couple
it_exists = False
# TODO do not push if local and distant records are exactly the same ?
# is_the_same_record = False
for distant_record in distant_records[record["type"]]: # # Flatten the DNS conf
if distant_record["type"] == record["type"] and distant_record["name"] == record["name"]: # flatten_dns_conf = []
it_exists = True # for key in dns_conf:
# previous TODO # list_of_records = dns_conf[key]
# if distant_record["ttl"] = ... and distant_record["name"] ... # for record in list_of_records:
# is_the_same_record = True # # FIXME Lexicon does not support CAA records
# # See https://github.com/AnalogJ/lexicon/issues/282 and https://github.com/AnalogJ/lexicon/pull/371
# # They say it's trivial to implement it!
# # And yet, it is still not done/merged
# if record["type"] != "CAA":
# # Add .domain.tdl to the name entry
# record["name"] = "{}.{}".format(record["name"], domain)
# flatten_dns_conf.append(record)
# Finally, push the new record or update the existing one # # Get provider info
record_config = { # # TODO
"action": "update" if it_exists else "create", # create, list, update, delete # provider = {
"type": record["type"], # specify a type for record filtering, case sensitive in some cases. # "name": "gandi",
"name": record["name"], # "options": {
"content": record["value"], # "api_protocol": "rest",
# FIXME Delte TTL, doesn't work with Gandi. # "auth_token": "vhcIALuRJKtoZiZyxfDYWLom"
# See https://github.com/AnalogJ/lexicon/issues/726 (similar issue) # }
# But I think there is another issue with Gandi. Or I'm misusing the API... # }
# "ttl": record["ttl"],
} # # Construct the base data structure to use lexicon's API.
final_lexicon = ConfigResolver().with_dict(dict_object=base_config).with_dict(dict_object=record_config) # base_config = {
client = Client(final_lexicon) # "provider_name": provider["name"],
print('pushed_record:', record_config, "", end=' ') # "domain": domain, # domain name
results = client.execute() # }
print('results:', results); # base_config[provider["name"]] = provider["options"]
# print("Failed" if results == False else "Ok")
# # Get types present in the generated records
# types = set()
# for record in flatten_dns_conf:
# types.add(record["type"])
# # Fetch all types present in the generated records
# distant_records = {}
# for key in types:
# record_config = {
# "action": "list",
# "type": key,
# }
# final_lexicon = ConfigResolver().with_dict(dict_object=base_config).with_dict(dict_object=record_config)
# # print('final_lexicon:', final_lexicon);
# client = Client(final_lexicon)
# distant_records[key] = client.execute()
# for key in types:
# for distant_record in distant_records[key]:
# print('distant_record:', distant_record);
# for local_record in flatten_dns_conf:
# print('local_record:', local_record);
# # Push the records
# for record in flatten_dns_conf:
# # For each record, first check if one record exists for the same (type, name) couple
# it_exists = False
# # TODO do not push if local and distant records are exactly the same ?
# # is_the_same_record = False
# for distant_record in distant_records[record["type"]]:
# if distant_record["type"] == record["type"] and distant_record["name"] == record["name"]:
# it_exists = True
# # previous TODO
# # if distant_record["ttl"] = ... and distant_record["name"] ...
# # is_the_same_record = True
# # Finally, push the new record or update the existing one
# record_config = {
# "action": "update" if it_exists else "create", # create, list, update, delete
# "type": record["type"], # specify a type for record filtering, case sensitive in some cases.
# "name": record["name"],
# "content": record["value"],
# # FIXME Delte TTL, doesn't work with Gandi.
# # See https://github.com/AnalogJ/lexicon/issues/726 (similar issue)
# # But I think there is another issue with Gandi. Or I'm misusing the API...
# # "ttl": record["ttl"],
# }
# final_lexicon = ConfigResolver().with_dict(dict_object=base_config).with_dict(dict_object=record_config)
# client = Client(final_lexicon)
# print('pushed_record:', record_config, "→", end=' ')
# results = client.execute()
# print('results:', results);
# # print("Failed" if results == False else "Ok")
# def domain_config_fetch(domain, key, value): # def domain_config_fetch(domain, key, value):