Hello community,
here is the log from the commit of package python-cloudflare for
openSUSE:Factory checked in at 2020-06-25 15:08:32
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-cloudflare (Old)
and /work/SRC/openSUSE:Factory/.python-cloudflare.new.3060 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-cloudflare"
Thu Jun 25 15:08:32 2020 rev:6 rq:816914 version:2.8.3
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-cloudflare/python-cloudflare.changes
2020-06-10 00:44:15.170310200 +0200
+++
/work/SRC/openSUSE:Factory/.python-cloudflare.new.3060/python-cloudflare.changes
2020-06-25 15:09:53.685829687 +0200
@@ -1,0 +2,7 @@
+Thu Jun 25 04:20:35 UTC 2020 - Steve Kowalik <[email protected]>
+
+- Update to 2.8.3:
+ * No upstream changelog
+- Add alternative for new manpage.
+
+-------------------------------------------------------------------
Old:
----
cloudflare-2.6.3.tar.gz
New:
----
cloudflare-2.8.3.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-cloudflare.spec ++++++
--- /var/tmp/diff_new_pack.sQOCkt/_old 2020-06-25 15:09:55.949836791 +0200
+++ /var/tmp/diff_new_pack.sQOCkt/_new 2020-06-25 15:09:55.953836804 +0200
@@ -18,7 +18,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-cloudflare
-Version: 2.6.3
+Version: 2.8.3
Release: 0
Summary: Python wrapper for the Cloudflare v4 API
License: MIT
@@ -52,21 +52,26 @@
%install
%python_install
+mkdir -p %{buildroot}%{_mandir}/man1
+mv %{buildroot}/usr/man/man1/cli4.man %{buildroot}%{_mandir}/man1/cli4.1
+rm -rf %{buildroot}/usr/man
%python_clone -a %{buildroot}%{_bindir}/cli4
+%python_clone -a %{buildroot}%{_mandir}/man1/cli4.1
# remove examples from sitelib
%python_expand rm -rf %{buildroot}%{$python_sitelib}/examples
%python_expand %fdupes %{buildroot}%{$python_sitelib}
%post
-%python_install_alternative cli4
+%python_install_alternative cli4 cli4.1
%postun
-%python_uninstall_alternative cli4
+%python_uninstall_alternative cli4 cli4.1
%files %{python_files}
%doc README.rst
%license LICENSE
%python_alternative %{_bindir}/cli4
+%python_alternative %{_mandir}/man1/cli4.1%{?ext_man}
%{python_sitelib}/*
%changelog
++++++ cloudflare-2.6.3.tar.gz -> cloudflare-2.8.3.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/CloudFlare/__init__.py
new/cloudflare-2.8.3/CloudFlare/__init__.py
--- old/cloudflare-2.6.3/CloudFlare/__init__.py 2020-02-09 21:37:52.000000000
+0100
+++ new/cloudflare-2.8.3/CloudFlare/__init__.py 2020-06-23 03:44:49.000000000
+0200
@@ -1,7 +1,7 @@
""" Cloudflare v4 API"""
from __future__ import absolute_import
-__version__ = '2.6.3'
+__version__ = '2.8.3'
from .cloudflare import CloudFlare
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/CloudFlare/api_decode_from_web.py
new/cloudflare-2.8.3/CloudFlare/api_decode_from_web.py
--- old/cloudflare-2.6.3/CloudFlare/api_decode_from_web.py 1970-01-01
01:00:00.000000000 +0100
+++ new/cloudflare-2.8.3/CloudFlare/api_decode_from_web.py 2020-06-19
23:59:12.000000000 +0200
@@ -0,0 +1,43 @@
+""" API extras for Cloudflare API"""
+
+from bs4 import BeautifulSoup, Comment
+
+API_TYPES = ['GET', 'POST', 'PATCH', 'PUT', 'DELETE']
+
+def do_section(section):
+ """ API extras for Cloudflare API"""
+
+ cmds = []
+ # look for deprecated first in section
+ deprecated = False
+ for tag2 in section.find_all('h3'):
+ if 'Deprecation Warning' in str(tag2):
+ deprecated = True
+ # look for all API calls in section
+ for tag2 in section.find_all('pre'):
+ cmd = []
+ for child in tag2.children:
+ if isinstance(child, Comment):
+ # remove <!-- react-text ... -> parts
+ continue
+ cmd.append(child.strip())
+ if len(cmd) == 0:
+ continue
+ action = cmd[0]
+ cmd = '/' + ''.join(cmd[1:])
+ if action == '' or action not in API_TYPES:
+ continue
+ v = {'deprecated': deprecated, 'action': action, 'cmd': cmd}
+ cmds.append(v)
+ return cmds
+
+def api_decode_from_web(content):
+ """ API extras for Cloudflare API"""
+
+ soup = BeautifulSoup(content, 'html.parser')
+
+ all_cmds = []
+ for section in soup.find_all('section'):
+ all_cmds += do_section(section)
+
+ return sorted(all_cmds, key=lambda v: v['cmd'])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/CloudFlare/api_v4.py
new/cloudflare-2.8.3/CloudFlare/api_v4.py
--- old/cloudflare-2.6.3/CloudFlare/api_v4.py 2020-02-09 21:23:59.000000000
+0100
+++ new/cloudflare-2.8.3/CloudFlare/api_v4.py 2020-06-17 21:27:12.000000000
+0200
@@ -24,6 +24,7 @@
zones_logpush(self)
zones_logs(self)
zones_media(self)
+ zones_origin_tls_client_auth(self)
zones_rate_limits(self)
zones_secondary_dns(self)
zones_settings(self)
@@ -48,12 +49,18 @@
# The API commands for /accounts/
accounts(self)
accounts_addressing(self)
+ accounts_audit_logs(self)
accounts_firewall(self)
+ accounts_load_balancers(self)
accounts_secondary_dns(self)
+ accounts_stream(self)
# The API commands for /memberships/
memberships(self)
+ # The API commands for /graphql
+ graphql(self)
+
def user(self):
""" API core commands for Cloudflare API"""
@@ -86,12 +93,14 @@
self.add('AUTH', "zones", "custom_certificates")
self.add('AUTH', "zones", "custom_certificates/prioritize")
self.add('AUTH', "zones", "custom_hostnames")
+ self.add('AUTH', "zones", "custom_hostnames/fallback_origin")
self.add('AUTH', "zones", "custom_pages")
self.add('AUTH', "zones", "dns_records")
self.add('AUTH', "zones", "dns_records/export")
self.add('AUTH', "zones", "dns_records/import")
self.add('AUTH', "zones", "filters")
self.add('AUTH', "zones", "healthchecks")
+ self.add('AUTH', "zones", "healthchecks/preview")
self.add('AUTH', "zones", "keyless_certificates")
self.add('AUTH', "zones", "pagerules")
self.add('AUTH', "zones", "pagerules/settings")
@@ -282,10 +291,20 @@
self.add('VOID', "zones", "ssl")
self.add('AUTH', "zones", "ssl/analyze")
self.add('AUTH', "zones", "ssl/certificate_packs")
+ self.add('AUTH', 'zones', 'ssl/certificate_packs/order')
+ self.add('AUTH', 'zones', 'ssl/certificate_packs/quota')
self.add('AUTH', "zones", "ssl/verification")
self.add('VOID', "zones", "ssl/universal")
self.add('AUTH', "zones", "ssl/universal/settings")
+def zones_origin_tls_client_auth(self):
+ """ API core commands for Cloudflare API"""
+
+ self.add('AUTH', 'zones', 'origin_tls_client_auth')
+ self.add('AUTH', 'zones', 'origin_tls_client_auth/hostnames')
+ self.add('AUTH', 'zones', 'origin_tls_client_auth/hostnames/certificates')
+ self.add('AUTH', 'zones', 'origin_tls_client_auth/settings')
+
def zones_workers(self):
""" API core commands for Cloudflare API"""
@@ -311,8 +330,11 @@
self.add('VOID', "user/load_balancers")
self.add('AUTH', "user/load_balancers/monitors")
+ self.add('AUTH', "user/load_balancers/monitors", "preview")
+ self.add('AUTH', "user/load_balancers/preview")
self.add('AUTH', "user/load_balancers/pools")
self.add('AUTH', "user/load_balancers/pools", "health")
+ self.add('AUTH', "user/load_balancers/pools", "preview")
def user_virtual_dns(self):
""" API core commands for Cloudflare API"""
@@ -351,8 +373,10 @@
def user_tokens_verify(self):
""" API core commands for Cloudflare API"""
- self.add('VOID', "user/tokens")
+ self.add('AUTH', "user/tokens")
+ self.add('AUTH', "user/tokens/permission_groups")
self.add('AUTH', "user/tokens/verify")
+ self.add('AUTH', "user/tokens", "value")
def organizations_audit_logs(self):
""" API core commands for Cloudflare API"""
@@ -372,10 +396,6 @@
self.add('VOID', "accounts", "billing")
self.add('AUTH', "accounts", "billing/profile")
self.add('AUTH', "accounts", "custom_pages")
- self.add('VOID', "accounts", "load_balancers")
- self.add('AUTH', "accounts", "load_balancers/monitors")
- self.add('AUTH', "accounts", "load_balancers/pools")
- self.add('AUTH', "accounts", "load_balancers/pools", "health")
self.add('AUTH', "accounts", "members")
self.add('AUTH', "accounts", "railguns")
self.add('AUTH', "accounts", "railguns/connections")
@@ -390,9 +410,6 @@
self.add('AUTH', "accounts", "storage/kv/namespaces", "bulk")
self.add('AUTH', "accounts", "storage/kv/namespaces", "keys")
self.add('AUTH', "accounts", "storage/kv/namespaces", "values")
- self.add('AUTH', "accounts", "stream")
- self.add('AUTH', "accounts", "stream/embed")
- self.add('AUTH', "accounts", "stream/preview")
self.add('AUTH', "accounts", "subscriptions")
self.add('AUTH', "accounts", "virtual_dns")
self.add('VOID', "accounts", "virtual_dns/dns_analytics")
@@ -408,6 +425,24 @@
self.add('AUTH', "accounts", "addressing/prefixes")
self.add('VOID', "accounts", "addressing/prefixes", "bgp")
self.add('AUTH', "accounts", "addressing/prefixes", "bgp/status")
+ self.add('AUTH', 'accounts', 'addressing/prefixes', 'delegations')
+
+def accounts_audit_logs(self):
+ """ API core commands for Cloudflare API"""
+
+ self.add('AUTH', "accounts", "audit_logs")
+
+def accounts_load_balancers(self):
+ """ API core commands for Cloudflare API"""
+
+ self.add('VOID', "accounts", "load_balancers")
+ self.add('AUTH', 'accounts', 'load_balancers/preview')
+ self.add('AUTH', "accounts", "load_balancers/monitors")
+ self.add('AUTH', 'accounts', 'load_balancers/monitors', 'preview')
+ self.add('AUTH', "accounts", "load_balancers/pools")
+ self.add('AUTH', "accounts", "load_balancers/pools", "health")
+ self.add('AUTH', 'accounts', 'load_balancers/pools', 'preview')
+ self.add('AUTH', 'accounts', 'load_balancers/search')
def accounts_firewall(self):
""" API core commands for Cloudflare API"""
@@ -423,6 +458,17 @@
self.add('AUTH', "accounts", "secondary_dns/masters")
self.add('AUTH', "accounts", "secondary_dns/tsigs")
+def accounts_stream(self):
+ """ API core commands for Cloudflare API"""
+
+ self.add('AUTH', "accounts", "stream")
+ self.add('AUTH', "accounts", "stream/copy")
+ self.add('AUTH', "accounts", "stream/direct_upload")
+ self.add('AUTH', "accounts", "stream/embed")
+ self.add('AUTH', "accounts", "stream/keys")
+ self.add('AUTH', "accounts", "stream/preview")
+ self.add('AUTH', "accounts", "stream", "captions")
+
def zones_media(self):
""" API core commands for Cloudflare API"""
@@ -434,3 +480,8 @@
""" API core commands for Cloudflare API"""
self.add('AUTH', "memberships")
+
+def graphql(self):
+ """ API core commands for Cloudflare API"""
+
+ self.add('AUTH', "graphql")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/CloudFlare/cloudflare.py
new/cloudflare-2.8.3/CloudFlare/cloudflare.py
--- old/cloudflare-2.6.3/CloudFlare/cloudflare.py 2020-02-06
02:31:46.000000000 +0100
+++ new/cloudflare-2.8.3/CloudFlare/cloudflare.py 2020-06-23
01:09:21.000000000 +0200
@@ -9,6 +9,7 @@
from .read_configs import read_configs
from .api_v4 import api_v4
from .api_extras import api_extras
+from .api_decode_from_web import api_decode_from_web
from .exceptions import CloudFlareError, CloudFlareAPIError,
CloudFlareInternalError
BASE_URL = 'https://api.cloudflare.com/client/v4'
@@ -154,6 +155,52 @@
identifier1, identifier2, identifier3,
params, data, files)
+ def _connection(self, method, url, headers=None, params=None,
data=None, files=None):
+ """ Cloudflare v4 API"""
+
+ if self.use_sessions:
+ if self.session is None:
+ self.session = requests.Session()
+ else:
+ self.session = requests
+
+ method = method.upper()
+
+ if method == 'GET':
+ return self.session.get(url,
+ headers=headers, params=params,
data=data)
+ if method == 'POST':
+ if isinstance(data, str):
+ return self.session.post(url,
+ headers=headers, params=params,
data=data, files=files)
+ else:
+ return self.session.post(url,
+ headers=headers, params=params,
json=data, files=files)
+ if method == 'PUT':
+ if isinstance(data, str):
+ return self.session.put(url,
+ headers=headers, params=params,
data=data)
+ else:
+ return self.session.put(url,
+ headers=headers, params=params,
json=data)
+ if method == 'DELETE':
+ if isinstance(data, str):
+ return self.session.delete(url,
+ headers=headers, params=params,
data=data)
+ else:
+ return self.session.delete(url,
+ headers=headers, params=params,
json=data)
+ if method == 'PATCH':
+ if isinstance(data, str):
+ return self.session.request('PATCH', url,
+ headers=headers,
params=params, data=data)
+ else:
+ return self.session.request('PATCH', url,
+ headers=headers,
params=params, json=data)
+
+ # should never happen
+ raise CloudFlareAPIError(0, 'method not supported')
+
def _network(self, method, headers, parts,
identifier1=None, identifier2=None, identifier3=None,
params=None, data=None, files=None):
@@ -208,72 +255,10 @@
self.logger.debug('Call: method and url %s %s', str(method),
str(url))
self.logger.debug('Call: headers %s',
str(sanitize_secrets(headers)))
- method = method.upper()
-
- if self.logger:
- self.logger.debug('Call: doit!')
-
- if self.use_sessions:
- if self.session is None:
- self.session = requests.Session()
- else:
- self.session = requests
-
try:
- if method == 'GET':
- response = self.session.get(url,
- headers=headers,
- params=params,
- data=data)
- elif method == 'POST':
- if isinstance(data, str):
- response = self.session.post(url,
- headers=headers,
- params=params,
- data=data,
- files=files)
- else:
- response = self.session.post(url,
- headers=headers,
- params=params,
- json=data,
- files=files)
- elif method == 'PUT':
- if isinstance(data, str):
- response = self.session.put(url,
- headers=headers,
- params=params,
- data=data)
- else:
- response = self.session.put(url,
- headers=headers,
- params=params,
- json=data)
- elif method == 'DELETE':
- if isinstance(data, str):
- response = self.session.delete(url,
- headers=headers,
- params=params,
- data=data)
- else:
- response = self.session.delete(url,
- headers=headers,
- params=params,
- json=data)
- elif method == 'PATCH':
- if isinstance(data, str):
- response = self.session.request('PATCH', url,
- headers=headers,
- params=params,
- data=data)
- else:
- response = self.session.request('PATCH', url,
- headers=headers,
- params=params,
- json=data)
- else:
- # should never happen
- raise CloudFlareAPIError(0, 'method not supported')
+ if self.logger:
+ self.logger.debug('Call: doit!')
+ response = self._connection(method, url, headers, params,
data, files)
if self.logger:
self.logger.debug('Call: done!')
except Exception as e:
@@ -296,7 +281,7 @@
response_type = 'application/octet-stream'
response_code = response.status_code
response_data = response.content
- if not isinstance(response_data, str):
+ if not isinstance(response_data, (str, bytes, bytearray)):
response_data = response_data.decode("utf-8")
if self.logger:
@@ -364,6 +349,9 @@
response_data = response_data.decode('utf-8')
try:
response_data = json.loads(response_data)
+ if not isinstance(response_data, (dict)):
+ response_data = {'success': True,
+ 'result': response_data}
except ValueError:
if response_data == '':
# This should really be 'null' but it isn't. Even
then, it's wrong!
@@ -397,23 +385,64 @@
# 3xx & 4xx errors - we should report that somehow - but
not quite yet
# response_data['code'] = response_code
pass
+ elif response_type == 'application/octet-stream' and
isinstance(response_data, (int, float)):
+ # It's binary data
+ if response_code == requests.codes.ok:
+ # 200 ok
+ response_data = {'success': True,
+ 'result': response_data}
+ else:
+ # 3xx & 4xx errors
+ response_data = {'success': False,
+ 'code': response_code,
+ 'result': response_data}
+ elif response_type == 'application/octet-stream' and
isinstance(response_data, (bytes, bytearray)):
+ # API says it's text; but maybe it's actually JSON? - should
be fixed in API
+ if hasattr(response_data, 'decode'):
+ response_data = response_data.decode('utf-8')
+ try:
+ response_data = json.loads(response_data)
+ if not isinstance(response_data, (dict)) or 'success' not
in response_data:
+ if response_code == requests.codes.ok:
+ # 200 ok
+ response_data = {'success': True,
+ 'result': response_data}
+ else:
+ # 3xx & 4xx errors
+ response_data = {'success': False,
+ 'code': response_code,
+ 'result': response_data}
+ except ValueError:
+ # So it wasn't JSON - moving on as if it's text!
+ # A single value is returned (vs an array or object)
+ if response_code == requests.codes.ok:
+ # 200 ok
+ response_data = {'success': True, 'result':
response_data}
+ else:
+ # 3xx & 4xx errors
+ response_data = {'success': False,
+ 'code': response_code,
+ 'result': response_data}
elif response_type == 'text/plain' or response_type ==
'application/octet-stream':
# API says it's text; but maybe it's actually JSON? - should
be fixed in API
if hasattr(response_data, 'decode'):
response_data = response_data.decode('utf-8')
try:
response_data = json.loads(response_data)
+ if not isinstance(response_data, (dict)):
+ response_data = {'success': True,
+ 'result': response_data}
except ValueError:
# So it wasn't JSON - moving on as if it's text!
# A single value is returned (vs an array or object)
if response_code == requests.codes.ok:
# 200 ok
- response_data = {'success': True, 'result':
str(response_data)}
+ response_data = {'success': True, 'result':
response_data}
else:
# 3xx & 4xx errors
response_data = {'success': False,
'code': response_code,
- 'result': str(response_data)}
+ 'result': response_data}
elif response_type == 'text/javascript' or response_type ==
'application/javascript':
# used by Cloudflare workers
if response_code == requests.codes.ok:
@@ -465,9 +494,29 @@
# Sanatize the returned results - just in case API is messed up
if 'success' not in response_data:
if 'errors' in response_data:
- if self.logger:
- self.logger.debug('Response: assuming success =
"False"')
- response_data['success'] = False
+ if response_data['errors'] == None:
+ # Only happens on /graphql call
+ if self.logger:
+ self.logger.debug('Response: assuming success =
"True"')
+ response_data['success'] = True
+ else:
+ if self.logger:
+ self.logger.debug('Response: assuming success =
"False"')
+ # The following only happens on /graphql call
+ try:
+ message = response_data['errors'][0]['message']
+ except:
+ message = ''
+ try:
+ location =
str(response_data['errors'][0]['location'])
+ except:
+ location = ''
+ try:
+ path = '>'.join(response_data['errors'][0]['path'])
+ except:
+ path = ''
+ response_data['errors'] = [{'code': 99999, 'message':
message + ' - ' + location + ' - ' + path}]
+ response_data['success'] = False
else:
if 'result' not in response_data:
# Only happens on /certificates call
@@ -484,8 +533,14 @@
response_data['success'] = True
if response_data['success'] is False:
- errors = response_data['errors'][0]
- code = errors['code']
+ if 'errors' in response_data:
+ errors = response_data['errors'][0]
+ else:
+ errors = {}
+ if 'code' in errors:
+ code = errors['code']
+ else:
+ code = 99998
if 'message' in errors:
message = errors['message']
elif 'error' in errors:
@@ -509,19 +564,25 @@
self.logger.debug('Response: error %d %s', code,
message)
raise CloudFlareAPIError(code, message)
- if self.logger:
- self.logger.debug('Response: %s', response_data['result'])
if self.raw:
result = {}
- # theres always a result value
- result['result'] = response_data['result']
+ # theres always a result value - unless it's a graphql query
+ try:
+ result['result'] = response_data['result']
+ except:
+ result['result'] = response_data
# theres may not be a result_info on every call
if 'result_info' in response_data:
result['result_info'] = response_data['result_info']
# no need to return success, errors, or messages as they
return via an exception
else:
- # theres always a result value
- result = response_data['result']
+ # theres always a result value - unless it's a graphql query
+ try:
+ result = response_data['result']
+ except:
+ result = response_data
+ if self.logger:
+ self.logger.debug('Response: %s', result)
return result
def _call_unwrapped(self, method, headers, parts,
@@ -537,6 +598,25 @@
result = response_data
return result
+ def _api_from_web(self):
+ """ Cloudflare v4 API"""
+
+ # base url isn't enough; we need less
+ url = '/'.join(self.base_url.split('/')[0:3])
+
+ try:
+ if self.logger:
+ self.logger.debug('Call: doit!')
+ response = self._connection("GET", url)
+ if self.logger:
+ self.logger.debug('Call: done!')
+ except Exception as e:
+ if self.logger:
+ self.logger.debug('Call: exception! "%s"' % (e))
+ raise CloudFlareAPIError(0, 'connection failed.')
+
+ return response.text
+
class _AddUnused(object):
""" Cloudflare v4 API"""
@@ -861,6 +941,11 @@
w = w + self.api_list(a, s + '/' + n)
return w
+ def api_from_web(self):
+ """ Cloudflare v4 API"""
+
+ return api_decode_from_web(self._base._api_from_web())
+
def __init__(self, email=None, token=None, certtoken=None, debug=False,
raw=False, use_sessions=True, profile=None):
""" Cloudflare v4 API"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/PKG-INFO
new/cloudflare-2.8.3/PKG-INFO
--- old/cloudflare-2.6.3/PKG-INFO 2020-02-09 22:14:52.411620000 +0100
+++ new/cloudflare-2.8.3/PKG-INFO 2020-06-23 03:46:31.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: cloudflare
-Version: 2.6.3
+Version: 2.8.3
Summary: Python wrapper for the Cloudflare v4 API
Home-page: https://github.com/cloudflare/python-cloudflare
Author: Martin J. Levy
@@ -491,6 +491,18 @@
cli4: /zones - 9103 Unknown X-Auth-Key or X-Auth-Email
$
+ More than one call can be done on the same command line. In this mode,
+ the connection is preserved between calls.
+
+ ::
+
+ $ cli4 /user/organizations /user/invites
+ ...
+ $
+
+ Note that the output is presently two JSON structures one after the
+ other - so less useful that you may think.
+
Finally, a command that provides more than one error response. This is
simulated by passing an invalid IPv4 address to a DNS record creation.
@@ -1000,7 +1012,7 @@
::
- $ python3 -m cli4 /user/workers/scripts
+ $ cli4 /user/workers/scripts
[
{
"created_on": "2018-02-15T00:00:00.000000Z",
@@ -1287,6 +1299,12 @@
`here <https://github.com/cloudflare-api/python-cloudflare-v4>`__. It
has been seriously expanded upon.
+ Changelog
+ ---------
+
+ An automatically generated CHANGELOG is provided
+ `here <CHANGELOG.md>`__.
+
Copyright
---------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/README.rst
new/cloudflare-2.8.3/README.rst
--- old/cloudflare-2.6.3/README.rst 2020-01-18 06:59:08.000000000 +0100
+++ new/cloudflare-2.8.3/README.rst 2020-06-20 07:53:59.000000000 +0200
@@ -483,6 +483,18 @@
cli4: /zones - 9103 Unknown X-Auth-Key or X-Auth-Email
$
+More than one call can be done on the same command line. In this mode,
+the connection is preserved between calls.
+
+::
+
+ $ cli4 /user/organizations /user/invites
+ ...
+ $
+
+Note that the output is presently two JSON structures one after the
+other - so less useful that you may think.
+
Finally, a command that provides more than one error response. This is
simulated by passing an invalid IPv4 address to a DNS record creation.
@@ -992,7 +1004,7 @@
::
- $ python3 -m cli4 /user/workers/scripts
+ $ cli4 /user/workers/scripts
[
{
"created_on": "2018-02-15T00:00:00.000000Z",
@@ -1279,6 +1291,12 @@
`here <https://github.com/cloudflare-api/python-cloudflare-v4>`__. It
has been seriously expanded upon.
+Changelog
+---------
+
+An automatically generated CHANGELOG is provided
+`here <CHANGELOG.md>`__.
+
Copyright
---------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/cli4/cli4.man
new/cloudflare-2.8.3/cli4/cli4.man
--- old/cloudflare-2.6.3/cli4/cli4.man 1970-01-01 01:00:00.000000000 +0100
+++ new/cloudflare-2.8.3/cli4/cli4.man 2020-04-28 23:07:08.000000000 +0200
@@ -0,0 +1,111 @@
+.TH CLI4 1
+
+.SH NAME
+cli4 \- Command line access to Cloudflare v4 API
+
+.SH SYNOPSIS
+.B cli4
+[\fB\-V\fR|\fB\-\-version]
+[\fB\-h\fR|\fB\-\-help]
+[\fB\-v\fR|\fB\-\-verbose]
+[\fB\-q\fR|\fB\-\-quiet]
+[\fB\-j\fR|\fB\-\-json]
+[\fB\-y\fR|\fB\-\-yaml]
+[\fB\-n\fR|\fB\-\-ndjson]
+[\fB\-r\fR|\fB\-\-raw]
+[\fB\-d\fR|\fB\-\-dump]
+[\fB\-p profile-name\fR|\fB\-\-profile profile-name]
+[\fBitem\fR=\fIvalue\fR ...]
+[\fBitem\fR=@\fIfilename\fR ...]
+[\fB\-G\fR|\fB\-\-get]
+[\fB\-P\fR|\fB\-\-patch]
+[\fB\-O\fR|\fB\-\-post]
+[\fB\-U\fR|\fB\-\-put]
+[\fB\-D\fR|\fB\-\-delete]
+.IR /command ...
+
+.SH DESCRIPTION
+.B cli4
+provides command line access to Cloudflare v4 API
+
+.SH OPTIONS
+.TP
+.IP "[\-V, \-\-version]"
+Display program version number and exit.
+.IP "[\-h, \-\-help]"
+This information (in a terse form).
+.IP "[\-v, \-\-verbose]"
+Provide some protcol debugging information.
+.IP "[\-q, \-\-quiet]"
+Don't output any JSON/YAML responses.
+.IP "[\-j, \-\-json]"
+Output response data in JSON format (the default).
+.IP "[\-y, \-\-yaml]"
+Output response data in YAML format (if yaml package installed).
+.IP "[\-n, \-\-ndjson]"
+Output response data in NDJSON format (if jsonlines package installed).
+.IP "[\-r, \-\-raw]"
+Output JSON results in raw mode without splitting out the errors and results.
+.IP "[\-d, \-\-dump]"
+Output a list of all API calls included in the code.
+.IP "[-p \fIprofile-name\fR, \-\-profile \fIprofile-name\fR]"
+Select a \fIprofile-name\fR from the configuration file (hence select custom
\fIemail\fR/\fItoken\fR values).
+.IP "\-\-get"
+Send HTTP request as a \fBGET\fR (the default).
+.IP "\-\-patch"
+Send HTTP request as a \fBPATCH\fR.
+.IP "\-\-post"
+Send HTTP request as a \fBPOST\fR.
+.IP "\-\-put"
+Send HTTP request as a \fBPUT\fR.
+.IP "\-\-delete"
+Send HTTP request as a \fBDELETE\fR.
+.IP "item=\fIvalue\fR"
+Set a paramater or data value to send with a \fBGET\fR, \fBPATCH\fR,
\fBPOST\fR, \fBPUT\fR or \fBDELETE\fR command. The value is sent as a string.
+.IP item:=\fIvalue\fR
+Set a paramater or data value to send with a \fBGET\fR, \fBPATCH\fR,
\fBPOST\fR, \fBPUT\fR or \fBDELETE\fR command. The value is sent as an interger.
+.IP item=@\fIfilename\fR
+Set a paramater or data value to send with a \fBPOST\fR or \fBPUT\fR command.
The value is based on the content of the file.
+.IP "\fI/command ...\fR"
+The API command(s) to execute.
+
+.SH COMMAND(S)
+The command string uses slash (\fB/\fR) to seperate the verbs in the same way
that the Cloudflare v4 API documentation does.
+Any verb starting with colon (\fB:\fR) is either converted to zone_id,
user_id, organtization_id, or otherwise.
+
+.SH RESULTS
+The output is either JSON or YAML formatted.
+
+.SH EXAMPLES
+.B cli4 /zones
+List infomation for all zones.
+
+.B cli4 /zones/:example.com
+List specific zone info.
+
+.B cli4 /zones/:example.com/settings
+List settings for a specific zone.
+
+.B cli4 --delete purge_everything=true /zones/:example.com/purge_cache
+Purge cache for a specific zone.
+
+.B cli4 --delete files='[http://example.com/css/styles.css]'
/zones/:example.com/purge_cache
+Purge cache for a specific zone.
+
+.B cli4 --delete
files='[http://example.com/css/styles.css,http://example.com/js/script.js]
/zones/:example.com/purge_cache
+Purge cache for a specific zone.
+
+.B cli4 --delete tags='[tag1,tag2,tag3]' /zones/:example.com/purge_cache
+Purge cache for a specific zone.
+
+.B cli4 /zones/:example.com/available_plans
+List available plans for a zone.
+
+.B cli4 --patch status=active /zones/:example.com/dnssec
+Make DNSSEC active for specfic zone.
+
+.B cli4 /zones/:example.com/dnssec
+List DNSSEC infomation and status for a specific zone.
+
+.SH SEE ALSO
+The Cloudflare API can be found https://api.cloudflare.com/. Each API call is
provided via a similarly named function within the Cloudflare class.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/cli4/cli4.py
new/cloudflare-2.8.3/cli4/cli4.py
--- old/cloudflare-2.6.3/cli4/cli4.py 2020-01-18 06:45:11.000000000 +0100
+++ new/cloudflare-2.8.3/cli4/cli4.py 2020-06-23 03:23:50.000000000 +0200
@@ -23,6 +23,15 @@
w = cf.api_list()
sys.stdout.write('\n'.join(w) + '\n')
+def dump_commands_from_web():
+ """dump a tree of all the known API commands - from web"""
+ cf = CloudFlare.CloudFlare()
+ w = cf.api_from_web()
+ for r in w:
+ if r['deprecated']:
+ continue
+ sys.stdout.write('%-6s %s\n' % (r['action'], r['cmd']))
+
def run_command(cf, method, command, params=None, content=None, files=None):
"""run the command line"""
# remove leading and trailing /'s
@@ -43,6 +52,7 @@
hex_only = re.compile('^[0-9a-fA-F]+$')
waf_rules = re.compile('^[0-9]+[A-Z]*$')
+ uuid_value =
re.compile('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$')
# 8-4-4-4-12
m = cf
for element in parts:
@@ -52,6 +62,9 @@
if len(element) in [32, 40, 48] and hex_only.match(element):
# raw identifier - lets just use it as-is
identifier1 = element
+ if len(element) == 36 and uuid_value.match(element):
+ # uuid identifier - lets just use it as-is
+ identifier1 = element
elif element[0] == ':':
# raw string - used for workers script_name - use
::script_name
identifier1 = element[1:]
@@ -77,12 +90,16 @@
else:
raise Exception("/%s/%s :NOT CODED YET" %
('/'.join(cmd), element))
except Exception as e:
- sys.exit('cli4: /%s - %s' % (command, e))
+ sys.stderr.write('cli4: /%s - %s\n' % (command, e))
+ raise e
cmd.append(':' + identifier1)
elif identifier2 is None:
if len(element) in [32, 40, 48] and hex_only.match(element):
# raw identifier - lets just use it as-is
identifier2 = element
+ if len(element) == 36 and uuid_value.match(element):
+ # uuid identifier - lets just use it as-is
+ identifier2 = element
elif element[0] == ':':
# raw string - used for workers script_names
identifier2 = element[1:]
@@ -92,10 +109,15 @@
identifier2 =
converters.convert_dns_record_to_identifier(cf,
identifier1,
element)
+ elif (cmd[0] and cmd[0] == 'zones') and (cmd[2] and
cmd[2] == 'custom_hostnames'):
+ identifier2 =
converters.convert_custom_hostnames_to_identifier(cf,
+
identifier1,
+
element)
else:
raise Exception("/%s/%s :NOT CODED YET" %
('/'.join(cmd), element))
except Exception as e:
- sys.exit('cli4: /%s - %s' % (command, e))
+ sys.stderr.write('cli4: /%s - %s\n' % (command, e))
+ raise e
# identifier2 may be an array - this needs to be dealt with
later
if isinstance(identifier2, list):
cmd.append(':' + '[' + ','.join(identifier2) + ']')
@@ -106,10 +128,17 @@
if len(element) in [32, 40, 48] and hex_only.match(element):
# raw identifier - lets just use it as-is
identifier3 = element
+ if len(element) == 36 and uuid_value.match(element):
+ # uuid identifier - lets just use it as-is
+ identifier3 = element
elif waf_rules.match(element):
identifier3 = element
else:
- sys.exit("/%s/%s :NOT CODED YET 3" % ('/'.join(cmd),
element))
+ if len(cmd) >= 6 and cmd[0] == 'accounts' and cmd[2] ==
'storage' and cmd[3] == 'kv' and cmd[4] == 'namespaces' and cmd[6] == 'values':
+ identifier3 = element
+ else:
+ sys.stderr.write('/%s/%s :NOT CODED YET 3\n' %
('/'.join(cmd), element))
+ raise e
else:
try:
m = getattr(m, element)
@@ -117,12 +146,14 @@
except AttributeError:
# the verb/element was not found
if len(cmd) == 0:
- sys.exit('cli4: /%s - not found' % (element))
+ sys.stderr.write('cli4: /%s - not found\n' % (element))
else:
- sys.exit('cli4: /%s/%s - not found' % ('/'.join(cmd),
element))
+ sys.stderr.write('cli4: /%s/%s - not found\n' %
('/'.join(cmd), element))
+ raise e
if content and params:
- sys.exit('cli4: /%s - content and params not allowed together' %
(command))
+ sys.stderr.write('cli4: /%s - content and params not allowed
together\n' % (command))
+ raise Exception
if content:
params = content
@@ -163,11 +194,14 @@
# more than one error returned by the API
for x in e:
sys.stderr.write('cli4: /%s - %d %s\n' % (command, x, x))
- sys.exit('cli4: /%s - %d %s' % (command, e, e))
+ sys.stderr.write('cli4: /%s - %d %s\n' % (command, e, e))
+ raise e
except CloudFlare.exceptions.CloudFlareInternalError as e:
- sys.exit('cli4: InternalError: /%s - %d %s' % (command, e, e))
+ sys.stderr.write('cli4: InternalError: /%s - %d %s\n' % (command,
e, e))
+ raise e
except Exception as e:
- sys.exit('cli4: /%s - %s - api error' % (command, e))
+ sys.stderr.write('cli4: /%s - %s - api error\n' % (command, e))
+ raise e
results.append(r)
return results
@@ -228,27 +262,29 @@
output = 'json'
raw = False
dump = False
+ dump_from_web = False
profile = None
method = 'GET'
usage = ('usage: cli4 '
+ '[-V|--version] [-h|--help] [-v|--verbose] [-q|--quiet] '
- + '[-j|--json] [-y|--yaml] [-n|ndjson]'
+ + '[-j|--json] [-y|--yaml] [-n|ndjson] '
+ '[-r|--raw] '
+ '[-d|--dump] '
+ + '[-a|--api] '
+ '[-p|--profile profile-name] '
+ '[--get|--patch|--post|--put|--delete] '
+ '[item=value|item=@filename|@filename ...] '
- + '/command...')
+ + '/command ...')
try:
opts, args = getopt.getopt(args,
- 'Vhvqjyrdp:GPOUD',
+ 'Vhvqjyrdap:GPOUD',
[
'version',
'help', 'verbose', 'quiet', 'json',
'yaml', 'ndjson',
'raw',
- 'dump',
+ 'dump', 'api',
'profile=',
'get', 'patch', 'post', 'put', 'delete'
])
@@ -279,6 +315,8 @@
profile = arg
elif opt in ('-d', '--dump'):
dump = True
+ elif opt in ('-a', '--api'):
+ dump_from_web = True
elif opt in ('-G', '--get'):
method = 'GET'
elif opt in ('-P', '--patch'):
@@ -294,6 +332,10 @@
dump_commands()
sys.exit(0)
+ if dump_from_web:
+ dump_commands_from_web()
+ sys.exit(0)
+
digits_only = re.compile('^-?[0-9]+$')
floats_only = re.compile('^-?[0-9.]+$')
@@ -306,8 +348,8 @@
if arg[0] == '@':
# a file to be uploaded - used in workers/script - only via PUT
filename = arg[1:]
- if method != 'PUT':
- sys.exit('cli4: %s - raw file upload only with PUT' %
(filename))
+ if method not in ['PUT','POST']:
+ sys.exit('cli4: %s - raw file upload only with PUT or POST' %
(filename))
try:
if filename == '-':
content = sys.stdin.read()
@@ -379,16 +421,22 @@
(tag_string, value_string))
# what's left is the command itself
- if len(args) != 1:
+ if len(args) < 1:
sys.exit(usage)
- command = args[0]
+ commands = args
try:
cf = CloudFlare.CloudFlare(debug=verbose, raw=raw, profile=profile)
except Exception as e:
sys.exit(e)
- results = run_command(cf, method, command, params, content, files)
- write_results(results, output)
+
+ for command in commands:
+ try:
+ results = run_command(cf, method, command, params, content, files)
+ write_results(results, output)
+ except Exception as e:
+ if len(commands) > 1:
+ continue
def cli4(args):
"""Cloudflare API via command line"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/cli4/converters.py
new/cloudflare-2.8.3/cli4/converters.py
--- old/cloudflare-2.6.3/cli4/converters.py 2020-01-16 19:14:19.000000000
+0100
+++ new/cloudflare-2.8.3/cli4/converters.py 2020-06-23 03:22:51.000000000
+0200
@@ -130,3 +130,23 @@
return p['id']
raise ConverterError('%s: not found' % (pool_name))
+
+def convert_custom_hostnames_to_identifier(cf, zone_id, custom_hostname):
+ """custom_hostnames to numbers"""
+ # this can return an array of results
+ params = {'name':custom_hostname}
+ try:
+ custom_hostnames_records = cf.zones.custom_hostnames.get(zone_id,
params=params)
+ except CloudFlare.exceptions.CloudFlareAPIError as e:
+ raise ConverterError(int(e), '%s - %d %s' % (dns_name, e, e))
+ except Exception as e:
+ raise ConverterError(0, '%s - %s' % (dns_name, e))
+
+ r = []
+ for custom_hostnames_record in custom_hostnames_records:
+ if custom_hostname == custom_hostnames_record['hostname']:
+ r.append(custom_hostnames_record['id'])
+ if len(r) > 0:
+ return r
+
+ raise ConverterError('%s: not found' % (custom_hostname))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/cloudflare.egg-info/PKG-INFO
new/cloudflare-2.8.3/cloudflare.egg-info/PKG-INFO
--- old/cloudflare-2.6.3/cloudflare.egg-info/PKG-INFO 2020-02-09
22:14:52.000000000 +0100
+++ new/cloudflare-2.8.3/cloudflare.egg-info/PKG-INFO 2020-06-23
03:46:31.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: cloudflare
-Version: 2.6.3
+Version: 2.8.3
Summary: Python wrapper for the Cloudflare v4 API
Home-page: https://github.com/cloudflare/python-cloudflare
Author: Martin J. Levy
@@ -491,6 +491,18 @@
cli4: /zones - 9103 Unknown X-Auth-Key or X-Auth-Email
$
+ More than one call can be done on the same command line. In this mode,
+ the connection is preserved between calls.
+
+ ::
+
+ $ cli4 /user/organizations /user/invites
+ ...
+ $
+
+ Note that the output is presently two JSON structures one after the
+ other - so less useful that you may think.
+
Finally, a command that provides more than one error response. This is
simulated by passing an invalid IPv4 address to a DNS record creation.
@@ -1000,7 +1012,7 @@
::
- $ python3 -m cli4 /user/workers/scripts
+ $ cli4 /user/workers/scripts
[
{
"created_on": "2018-02-15T00:00:00.000000Z",
@@ -1287,6 +1299,12 @@
`here <https://github.com/cloudflare-api/python-cloudflare-v4>`__. It
has been seriously expanded upon.
+ Changelog
+ ---------
+
+ An automatically generated CHANGELOG is provided
+ `here <CHANGELOG.md>`__.
+
Copyright
---------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/cloudflare.egg-info/SOURCES.txt
new/cloudflare-2.8.3/cloudflare.egg-info/SOURCES.txt
--- old/cloudflare-2.6.3/cloudflare.egg-info/SOURCES.txt 2020-02-09
22:14:52.000000000 +0100
+++ new/cloudflare-2.8.3/cloudflare.egg-info/SOURCES.txt 2020-06-23
03:46:31.000000000 +0200
@@ -4,6 +4,7 @@
setup.cfg
setup.py
CloudFlare/__init__.py
+CloudFlare/api_decode_from_web.py
CloudFlare/api_extras.py
CloudFlare/api_v4.py
CloudFlare/cloudflare.py
@@ -13,6 +14,7 @@
CloudFlare/utils.py
cli4/__init__.py
cli4/__main__.py
+cli4/cli4.man
cli4/cli4.py
cli4/converters.py
cloudflare.egg-info/PKG-INFO
@@ -27,10 +29,12 @@
examples/example_are_zones_ipv6_simple.py
examples/example_certificates.py
examples/example_create_zone_and_populate.py
+examples/example_custom_hostnames.py
examples/example_delete_zone_entry.py
examples/example_dns_export.py
examples/example_dnssec_settings.py
examples/example_ips.py
+examples/example_list_api_from_web.py
examples/example_page_rules.sh
examples/example_paging_thru_zones.py
examples/example_paging_thru_zones.sh
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/cloudflare.egg-info/requires.txt
new/cloudflare-2.8.3/cloudflare.egg-info/requires.txt
--- old/cloudflare-2.6.3/cloudflare.egg-info/requires.txt 2020-02-09
22:14:52.000000000 +0100
+++ new/cloudflare-2.8.3/cloudflare.egg-info/requires.txt 2020-06-23
03:46:31.000000000 +0200
@@ -1,4 +1,4 @@
requests
-future
pyyaml
jsonlines
+beautifulsoup4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/cloudflare-2.6.3/examples/example_always_use_https.py
new/cloudflare-2.8.3/examples/example_always_use_https.py
--- old/cloudflare-2.6.3/examples/example_always_use_https.py 2019-11-20
19:33:03.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_always_use_https.py 2020-06-22
21:30:28.000000000 +0200
@@ -1,8 +1,6 @@
#!/usr/bin/env python
"""Cloudflare API code - example"""
-from __future__ import print_function
-
import os
import sys
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/examples/example_are_zones_ipv6.py
new/cloudflare-2.8.3/examples/example_are_zones_ipv6.py
--- old/cloudflare-2.6.3/examples/example_are_zones_ipv6.py 2019-11-20
19:33:03.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_are_zones_ipv6.py 2020-06-22
21:30:32.000000000 +0200
@@ -1,8 +1,6 @@
#!/usr/bin/env python
"""Cloudflare API code - example"""
-from __future__ import print_function
-
import os
import sys
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/cloudflare-2.6.3/examples/example_are_zones_ipv6_simple.py
new/cloudflare-2.8.3/examples/example_are_zones_ipv6_simple.py
--- old/cloudflare-2.6.3/examples/example_are_zones_ipv6_simple.py
2019-11-20 19:33:03.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_are_zones_ipv6_simple.py
2020-06-22 21:30:35.000000000 +0200
@@ -1,8 +1,6 @@
#!/usr/bin/env python
"""Cloudflare API code - example"""
-from __future__ import print_function
-
import os
import sys
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/examples/example_certificates.py
new/cloudflare-2.8.3/examples/example_certificates.py
--- old/cloudflare-2.6.3/examples/example_certificates.py 2019-11-20
19:33:03.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_certificates.py 2020-06-22
21:32:47.000000000 +0200
@@ -1,11 +1,8 @@
#!/usr/bin/env python
"""Cloudflare API code - example"""
-from __future__ import print_function
-
import os
import sys
-import json
sys.path.insert(0, os.path.abspath('..'))
import CloudFlare
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/cloudflare-2.6.3/examples/example_create_zone_and_populate.py
new/cloudflare-2.8.3/examples/example_create_zone_and_populate.py
--- old/cloudflare-2.6.3/examples/example_create_zone_and_populate.py
2019-11-20 19:33:03.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_create_zone_and_populate.py
2020-06-22 21:30:46.000000000 +0200
@@ -1,8 +1,6 @@
#!/usr/bin/env python
"""Cloudflare API code - example"""
-from __future__ import print_function
-
import os
import sys
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/cloudflare-2.6.3/examples/example_custom_hostnames.py
new/cloudflare-2.8.3/examples/example_custom_hostnames.py
--- old/cloudflare-2.6.3/examples/example_custom_hostnames.py 1970-01-01
01:00:00.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_custom_hostnames.py 2020-06-23
03:43:49.000000000 +0200
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+"""Cloudflare API code - example"""
+
+import os
+import sys
+
+sys.path.insert(0, os.path.abspath('..'))
+import CloudFlare
+
+def main():
+ """Cloudflare API code - example"""
+
+ # Grab the first argument, if there is one
+ try:
+ zone_name = sys.argv[1]
+ params = {'name':zone_name, 'per_page':1}
+ except IndexError:
+ params = {'per_page':500}
+
+ cf = CloudFlare.CloudFlare()
+
+ # grab the zone identifier
+ try:
+ zones = cf.zones.get(params=params)
+ except CloudFlare.exceptions.CloudFlareAPIError as e:
+ exit('/zones.get %d %s - api call failed' % (e, e))
+ except Exception as e:
+ exit('/zones.get - %s - api call failed' % (e))
+
+ # there should only be one zone - but handle more if needed
+ for zone in sorted(zones, key=lambda v: v['name']):
+ zone_name = zone['name']
+ zone_id = zone['id']
+ zone_plan = zone['plan']['name']
+ if zone_plan != 'Enterprise Website':
+ print('%s %s %s - not Enterprise' % (zone_id, zone_name,
zone_plan))
+ continue
+
+ print('%s %-40s %s' % (zone_id, zone_name, zone_plan))
+ try:
+ custom_info = cf.zones.custom_hostnames.get(zone_id)
+ except CloudFlare.exceptions.CloudFlareAPIError as e:
+ sys.stderr.write('/zones.custom_hostnames.get %d %s - api call
failed\n' % (e, e))
+ continue
+
+ for hostname_info in custom_info:
+ print('\t%s %-30s %s' % (hostname_info['id'],
hostname_info['hostname'], hostname_info['created_at']))
+
+ for s in sorted(hostname_info.keys()):
+ if s in ['id', 'hostname', 'created_at'] or hostname_info[s]
== None:
+ continue
+ print('\t%-15s = %s' % (s, hostname_info[s]))
+
+ try:
+ fallback_origin =
cf.zones.custom_hostnames.fallback_origin.get(zone_id)
+ except CloudFlare.exceptions.CloudFlareAPIError as e:
+ sys.stderr.write('/zones.custom_hostnames.fallback_origin.get %d
%s - api call failed\n' % (e, e))
+ continue
+
+ print('\t%s %-30s %s %s' % ('', fallback_origin['origin'],
fallback_origin['created_at'], fallback_origin['status']))
+ print('')
+
+ exit(0)
+
+if __name__ == '__main__':
+ main()
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/cloudflare-2.6.3/examples/example_delete_zone_entry.py
new/cloudflare-2.8.3/examples/example_delete_zone_entry.py
--- old/cloudflare-2.6.3/examples/example_delete_zone_entry.py 2019-11-20
19:33:03.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_delete_zone_entry.py 2020-06-22
21:33:41.000000000 +0200
@@ -1,13 +1,8 @@
#!/usr/bin/env python
"""Cloudflare API code - example"""
-from __future__ import print_function
-
import os
import sys
-import re
-import json
-import requests
sys.path.insert(0, os.path.abspath('..'))
import CloudFlare
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/examples/example_dns_export.py
new/cloudflare-2.8.3/examples/example_dns_export.py
--- old/cloudflare-2.6.3/examples/example_dns_export.py 2019-11-20
19:33:03.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_dns_export.py 2020-06-22
21:30:58.000000000 +0200
@@ -1,8 +1,6 @@
#!/usr/bin/env python
"""Cloudflare API code - example"""
-from __future__ import print_function
-
import os
import sys
sys.path.insert(0, os.path.abspath('..'))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/examples/example_dnssec_settings.py
new/cloudflare-2.8.3/examples/example_dnssec_settings.py
--- old/cloudflare-2.6.3/examples/example_dnssec_settings.py 2019-11-20
19:33:03.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_dnssec_settings.py 2020-06-22
21:31:06.000000000 +0200
@@ -1,8 +1,6 @@
#!/usr/bin/env python
"""Cloudflare API code - example"""
-from __future__ import print_function
-
import os
import sys
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/examples/example_ips.py
new/cloudflare-2.8.3/examples/example_ips.py
--- old/cloudflare-2.6.3/examples/example_ips.py 2019-11-20
19:33:03.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_ips.py 2020-06-22
21:30:09.000000000 +0200
@@ -1,8 +1,6 @@
#!/usr/bin/env python
"""Cloudflare API code - example"""
-from __future__ import print_function
-
import os
import sys
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/cloudflare-2.6.3/examples/example_list_api_from_web.py
new/cloudflare-2.8.3/examples/example_list_api_from_web.py
--- old/cloudflare-2.6.3/examples/example_list_api_from_web.py 1970-01-01
01:00:00.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_list_api_from_web.py 2020-06-22
21:31:11.000000000 +0200
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+"""Cloudflare API code - example"""
+
+import os
+import sys
+import json
+
+sys.path.insert(0, os.path.abspath('..'))
+import CloudFlare
+
+def main():
+ """Cloudflare API code - example"""
+
+ cf = CloudFlare.CloudFlare()
+ try:
+ r = cf.api_from_web()
+ except Exception as e:
+ exit('api_from_web: - %s - api call connection failed' % (e))
+
+ print(json.dumps(r))
+ exit(0)
+
+if __name__ == '__main__':
+ main()
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/cloudflare-2.6.3/examples/example_paging_thru_zones.py
new/cloudflare-2.8.3/examples/example_paging_thru_zones.py
--- old/cloudflare-2.6.3/examples/example_paging_thru_zones.py 2019-11-20
19:33:03.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_paging_thru_zones.py 2020-06-22
21:31:15.000000000 +0200
@@ -1,8 +1,6 @@
#!/usr/bin/env python
"""Cloudflare API code - example"""
-from __future__ import print_function
-
import os
import sys
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/examples/example_proxied.py
new/cloudflare-2.8.3/examples/example_proxied.py
--- old/cloudflare-2.6.3/examples/example_proxied.py 2019-11-20
19:33:03.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_proxied.py 2020-06-22
21:31:19.000000000 +0200
@@ -1,8 +1,6 @@
#!/usr/bin/env python
"""Cloudflare API code - example"""
-from __future__ import print_function
-
import os
import sys
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/examples/example_settings.py
new/cloudflare-2.8.3/examples/example_settings.py
--- old/cloudflare-2.6.3/examples/example_settings.py 2019-11-20
19:33:03.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_settings.py 2020-06-22
21:31:25.000000000 +0200
@@ -1,8 +1,6 @@
#!/usr/bin/env python
"""Cloudflare API code - example"""
-from __future__ import print_function
-
import os
import sys
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/cloudflare-2.6.3/examples/example_update_dynamic_dns.py
new/cloudflare-2.8.3/examples/example_update_dynamic_dns.py
--- old/cloudflare-2.6.3/examples/example_update_dynamic_dns.py 2020-02-09
21:28:33.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_update_dynamic_dns.py 2020-06-22
21:36:58.000000000 +0200
@@ -1,12 +1,8 @@
#!/usr/bin/env python
"""Cloudflare API code - example"""
-from __future__ import print_function
-
import os
import sys
-import re
-import json
import requests
sys.path.insert(0, os.path.abspath('..'))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/examples/example_user.py
new/cloudflare-2.8.3/examples/example_user.py
--- old/cloudflare-2.6.3/examples/example_user.py 2019-11-20
19:33:03.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_user.py 2020-06-22
21:37:32.000000000 +0200
@@ -1,11 +1,8 @@
#!/usr/bin/env python
"""Cloudflare API code - example"""
-from __future__ import print_function
-
import os
import sys
-import json
sys.path.insert(0, os.path.abspath('..'))
import CloudFlare
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/examples/example_with_usage.py
new/cloudflare-2.8.3/examples/example_with_usage.py
--- old/cloudflare-2.6.3/examples/example_with_usage.py 2019-11-20
19:33:03.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_with_usage.py 2020-06-22
21:31:40.000000000 +0200
@@ -1,8 +1,6 @@
#!/usr/bin/env python
"""Cloudflare API code - example"""
-from __future__ import print_function
-
import os
import sys
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/examples/example_zone_search.sh
new/cloudflare-2.8.3/examples/example_zone_search.sh
--- old/cloudflare-2.6.3/examples/example_zone_search.sh 2020-01-22
01:25:22.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_zone_search.sh 2020-04-09
02:38:19.000000000 +0200
@@ -3,23 +3,25 @@
ZONE=${1-example.com}
EXTRA=${2}
-while read search_type
+SEARCH_TYPES="
+ equal
+ not_equal
+ greater_than
+ less_than
+ starts_with
+ ends_with
+ contains
+ starts_with_case_sensitive
+ ends_with_case_sensitive
+ contains_case_sensitive
+ list_contains
+"
+
+for search_type in ${SEARCH_TYPES}
do
echo TRY: "name=${search_type}:${ZONE}"
cli4 per_page=50 name="${search_type}:${ZONE}" ${EXTRA} /zones/ | jq -r
'.[]|.id,.name' | paste - -
-done <<!!
-equal
-not_equal
-greater_than
-less_than
-starts_with
-ends_with
-contains
-starts_with_case_sensitive
-ends_with_case_sensitive
-contains_case_sensitive
-list_contains
-!!
+done
exit 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/examples/example_zones.py
new/cloudflare-2.8.3/examples/example_zones.py
--- old/cloudflare-2.6.3/examples/example_zones.py 2019-11-20
19:33:03.000000000 +0100
+++ new/cloudflare-2.8.3/examples/example_zones.py 2020-06-22
23:24:44.000000000 +0200
@@ -1,8 +1,6 @@
#!/usr/bin/env python
"""Cloudflare API code - example"""
-from __future__ import print_function
-
import os
import sys
import re
@@ -34,28 +32,41 @@
for zone in sorted(zones, key=lambda v: v['name']):
zone_name = zone['name']
zone_id = zone['id']
+ zone_type = zone['type']
if 'email' in zone['owner']:
zone_owner = zone['owner']['email']
else:
zone_owner = '"' + zone['owner']['name'] + '"'
zone_plan = zone['plan']['name']
+ print('%s %-35s %-30s %-20s %s' % (zone_id, zone_name, zone_type,
zone_owner, zone_plan))
+
try:
dns_records = cf.zones.dns_records.get(zone_id)
except CloudFlare.exceptions.CloudFlareAPIError as e:
- exit('/zones/dns_records %d %s - api call failed' % (e, e))
-
- print(zone_id, zone_name, zone_owner, zone_plan)
+ sys.stderr.write('/zones/dns_records %d %s - api call failed\n' %
(e, e))
+ continue
prog = re.compile('\.*'+zone_name+'$')
dns_records = sorted(dns_records, key=lambda v: prog.sub('',
v['name']) + '_' + v['type'])
for dns_record in dns_records:
- r_name = dns_record['name']
- r_type = dns_record['type']
- r_value = dns_record['content']
- r_ttl = dns_record['ttl']
- r_id = dns_record['id']
- print('\t%s %60s %6d %-5s %s' % (r_id, r_name, r_ttl, r_type,
r_value))
+ r_name = dns_record['name']
+ r_type = dns_record['type']
+ if 'content' in dns_record:
+ r_value = dns_record['content']
+ else:
+ # should not happen
+ r_value = ''
+ if 'priority' in dns_record:
+ r_priority = dns_record['priority']
+ else:
+ r_priority = ''
+ r_ttl = dns_record['ttl']
+ if zone_type == 'secondary':
+ r_id = 'secondary'
+ else:
+ r_id = dns_record['id']
+ print('\t%s %60s %6d %-5s %4s %s' % (r_id, r_name, r_ttl,
r_type, r_priority, r_value))
print('')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cloudflare-2.6.3/setup.py
new/cloudflare-2.8.3/setup.py
--- old/cloudflare-2.6.3/setup.py 2020-01-14 05:22:19.000000000 +0100
+++ new/cloudflare-2.8.3/setup.py 2020-06-22 21:29:07.000000000 +0200
@@ -31,8 +31,8 @@
#package_dir={'CloudFlare/examples': 'examples'},
#package_data={'cloudflare-examples': ["examples/*"]},
include_package_data=True,
- #data_files = [('man/man1', ['cli4/cli4.man'])],
- install_requires=['requests', 'future', 'pyyaml', 'jsonlines'],
+ data_files = [('man/man1', ['cli4/cli4.man'])],
+ install_requires=['requests', 'pyyaml', 'jsonlines', 'beautifulsoup4'],
keywords='cloudflare',
entry_points={
'console_scripts': [