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
help: Delete the key
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 #

View file

@ -1,3 +1,114 @@
# This file is automatically generated
# during Debian's package build by the script
# data/actionsmap/yunohost_completion.py
#
# completion for yunohost
# 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_secret
- aurora
aurora:
- auth_api_key
- auth_secret_key
- azure
azure:
- auth_client_id
- auth_client_secret
- auth_tenant_id
- auth_subscription_id
- resource_group
- cloudflare
cloudflare:
- auth_username
- auth_token
- zone_id
- cloudns
cloudns:
- auth_id
- auth_subid
- auth_subuser
- auth_password
- weight
- port
- cloudxns
cloudxns:
- auth_username
- auth_token
- conoha
conoha:
- auth_region
- auth_token
- auth_username
- auth_password
- auth_tenant_id
- constellix
constellix:
- auth_username
- auth_token
- digitalocean
digitalocean:
- auth_token
- dinahosting
dinahosting:
- auth_username
- auth_password
- directadmin
directadmin:
- auth_password
- auth_username
- endpoint
- dnsimple
dnsimple:
- auth_token
- auth_username
- auth_password
- auth_2fa
- dnsmadeeasy
dnsmadeeasy:
- auth_username
- auth_token
- dnspark
dnspark:
- auth_username
- auth_token
- dnspod
dnspod:
- auth_username
- auth_token
- dreamhost
dreamhost:
- auth_token
- dynu
dynu:
- auth_token
- easydns
easydns:
- auth_username
- auth_token
- easyname
easyname:
- auth_username
- auth_password
- euserv
euserv:
- auth_username
- auth_password
- exoscale
exoscale:
- auth_key
- auth_secret
- gandi
gandi:
- auth_token
- api_protocol
- gehirn
gehirn:
- auth_token
- auth_secret
- glesys
glesys:
- auth_username
- auth_token
- godaddy
godaddy:
- auth_key
- auth_secret
- googleclouddns
googleclouddns:
- auth_service_account_info
- gransy
gransy:
- auth_username
- auth_password
- gratisdns
gratisdns:
- auth_username
- auth_password
- henet
henet:
- auth_username
- auth_password
- hetzner
hetzner:
- auth_token
- hostingde
hostingde:
- auth_token
- hover
hover:
- auth_username
- auth_password
- infoblox
infoblox:
- auth_user
- auth_psw
- ib_view
- ib_host
- infomaniak
infomaniak:
- auth_token
- internetbs
internetbs:
- auth_key
- auth_password
- inwx
inwx:
- auth_username
- auth_password
- joker
joker:
- auth_token
- linode
linode:
- auth_token
- linode4
linode4:
- auth_token
- localzone
localzone:
- filename
- luadns
luadns:
- auth_username
- auth_token
- memset
memset:
- auth_token
- mythicbeasts
mythicbeasts:
- auth_username
- auth_password
- auth_token
- namecheap
namecheap:
- auth_token
- auth_username
- auth_client_ip
- auth_sandbox
- namesilo
namesilo:
- auth_token
- netcup
netcup:
- auth_customer_id
- auth_api_key
- auth_api_password
- nfsn
nfsn:
- auth_username
- auth_token
- njalla
njalla:
- auth_token
- nsone
nsone:
- auth_token
- onapp
onapp:
- auth_username
- auth_token
- auth_server
- online
online:
- auth_token
- ovh
ovh:
- auth_entrypoint
- auth_application_key
- auth_application_secret
- auth_consumer_key
- plesk
plesk:
- auth_username
- auth_password
- plesk_server
- pointhq
pointhq:
- auth_username
- auth_token
- powerdns
powerdns:
- auth_token
- pdns_server
- pdns_server_id
- pdns_disable_notify
- rackspace
rackspace:
- auth_account
- auth_username
- auth_api_key
- auth_token
- sleep_time
- rage4
rage4:
- auth_username
- auth_token
- rcodezero
rcodezero:
- auth_token
- route53
route53:
- auth_access_key
- auth_access_secret
- private_zone
- auth_username
- auth_token
- safedns
safedns:
- auth_token
- sakuracloud
sakuracloud:
- auth_token
- auth_secret
- softlayer
softlayer:
- auth_username
- auth_api_key
- transip
transip:
- auth_username
- auth_api_key
- ultradns
ultradns:
- auth_token
- auth_username
- auth_password
- vultr
vultr:
- auth_token
- yandex
yandex:
- auth_token
- zeit
zeit:
- auth_token
- zilore
zilore:
- auth_key
- zonomi
zonomi:
- auth_token
- auth_entrypoint

View file

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

View file

@ -29,8 +29,8 @@ import sys
import yaml
import functools
from lexicon.config import ConfigResolver
from lexicon.client import Client
# from lexicon.config import ConfigResolver
# from lexicon.client import Client
from moulinette import m18n, msettings, msignals
from moulinette.core import MoulinetteError
@ -43,6 +43,7 @@ from yunohost.app import (
_installed_apps,
_get_app_settings,
_get_conflicting_apps,
_parse_args_in_yunohost_format
)
from yunohost.regenconf import regen_conf, _force_clear_hashes, _process_regen_conf
from yunohost.utils.network import get_public_ip
@ -53,6 +54,7 @@ from yunohost.hook import hook_callback
logger = getActionLogger("yunohost.domain")
DOMAIN_SETTINGS_PATH = "/etc/yunohost/domains.yml"
REGISTRAR_LIST_PATH = "/usr/share/yunohost/other/providers_list.yml"
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:
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):
"""
Send DNS records to the previously-configured registrar of the domain.
"""
# Generate the records
if domain not in domain_list()["domains"]:
raise YunohostValidationError("domain_name_unknown", domain=domain)
registrars = yaml.load(open(REGISTRAR_LIST_PATH, "r+"))
if not registrar in registrars.keys():
# FIXME créer l'erreur
raise YunohostError("registrar_unknown")
parameters = registrars[registrar]
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)
dns_conf = _build_dns_conf(domains_settings)
# 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"
domain_provider = {
'name': registrar,
'options': {
}
}
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.
base_config = {
"provider_name": provider["name"],
"domain": domain, # domain name
}
base_config[provider["name"]] = provider["options"]
# Save the settings to the .yaml file
with open(DOMAIN_SETTINGS_PATH, 'w') as file:
yaml.dump(domains, file, default_flow_style=False)
# 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()
# def domain_push_config(domain):
# """
# Send DNS records to the previously-configured registrar of the domain.
# """
# # Generate the records
# if domain not in domain_list()["domains"]:
# raise YunohostValidationError("domain_name_unknown", domain=domain)
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);
# domains_settings = _get_domain_settings(domain, True)
# 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
# dns_conf = _build_dns_conf(domains_settings)
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
# # 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)
# 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")
# # Get provider info
# # TODO
# provider = {
# "name": "gandi",
# "options": {
# "api_protocol": "rest",
# "auth_token": "vhcIALuRJKtoZiZyxfDYWLom"
# }
# }
# # Construct the base data structure to use lexicon's API.
# base_config = {
# "provider_name": provider["name"],
# "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:
# 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):