1cloudflare-python
2=================
3
4Installation
5------------
6
7Two methods are provided to install this software. Use PyPi (see
8`package <https://pypi.python.org/pypi/cloudflare>`__ details) or GitHub
9(see `package <https://github.com/cloudflare/python-cloudflare>`__
10details).
11
12Via PyPI
13~~~~~~~~
14
15.. code:: bash
16
17 $ sudo pip install cloudflare
18 $
19
20Yes - that simple! (the sudo may not be needed in some cases).
21
22Via github
23~~~~~~~~~~
24
25.. code:: bash
26
27 $ git clone https://github.com/cloudflare/python-cloudflare
28 $ cd python-cloudflare
29 $ ./setup.py build
30 $ sudo ./setup.py install
31 $
32
33Or whatever variance of that you want to use. There is a Makefile
34included.
35
36Cloudflare name change - dropping the capital F
37-----------------------------------------------
38
39In Sepember/October 2016 the company modified its company name and
40dropped the capital F. However, for now (and for backward compatibility
41reasons) the class name stays the same.
42
43Cloudflare API version 4
44------------------------
45
46The Cloudflare API can be found `here <https://api.cloudflare.com/>`__.
47Each API call is provided via a similarly named function within the
48**CloudFlare** class. A full list is provided below.
49
50Example code
51------------
52
53All example code is available on GitHub (see
54`package <https://github.com/cloudflare/python-cloudflare>`__ in the
55`examples <https://github.com/cloudflare/python-cloudflare/tree/master/examples>`__
56folder).
57
58Blog
59----
60
61This package was initially introduced
62`here <https://blog.cloudflare.com/python-cloudflare/>`__ via
63Cloudflare's `blog <https://blog.cloudflare.com/>`__.
64
65Getting Started
66---------------
67
68A very simple listing of zones within your account; including the IPv6
69status of the zone.
70
71.. code:: python
72
73 import CloudFlare
74
75 def main():
76 cf = CloudFlare.CloudFlare()
77 zones = cf.zones.get()
78 for zone in zones:
79 zone_id = zone['id']
80 zone_name = zone['name']
81 print zone_id, zone_name
82
83 if __name__ == '__main__':
84 main()
85
86This example works when there are less than 50 zones (50 is the default
87number of values returned from a query like this).
88
89Now lets expand on that and add code to show the IPv6 and SSL status of
90the zones. Lets also query 100 zones.
91
92.. code:: python
93
94 import CloudFlare
95
96 def main():
97 cf = CloudFlare.CloudFlare()
98 zones = cf.zones.get(params = {'per_page':100})
99 for zone in zones:
100 zone_id = zone['id']
101 zone_name = zone['name']
102
103 settings_ssl = cf.zones.settings.ssl.get(zone_id)
104 ssl_status = settings_ssl['value']
105
106 settings_ipv6 = cf.zones.settings.ipv6.get(zone_id)
107 ipv6_status = settings_ipv6['value']
108
109 print zone_id, zone_name, ssl_status, ipv6_status
110
111 if __name__ == '__main__':
112 main()
113
114In order to query more than a single page of zones, we would have to use
115the raw mode (described more below). We can loop over many get calls and
116pass the page parameter to facilitate the paging.
117
118Raw mode is only needed when a get request has the possibility of
119returning many items.
120
121.. code:: python
122
123 import CloudFlare
124
125 def main():
126 cf = CloudFlare.CloudFlare(raw=True)
127 page_number = 0
128 while True:
129 page_number += 1
130 raw_results = cf.zones.get(params={'per_page':5,'page':page_number})
131 zones = raw_results['result']
132
133 for zone in zones:
134 zone_id = zone['id']
135 zone_name = zone['name']
136 print zone_id, zone_name
137
138 total_pages = raw_results['result_info']['total_pages']
139 if page_number == total_pages:
140 break
141
142 if __name__ == '__main__':
143 main()
144
145A more complex example follows.
146
147.. code:: python
148
149 import CloudFlare
150
151 def main():
152 zone_name = 'example.com'
153
154 cf = CloudFlare.CloudFlare()
155
156 # query for the zone name and expect only one value back
157 try:
158 zones = cf.zones.get(params = {'name':zone_name,'per_page':1})
159 except CloudFlare.exceptions.CloudFlareAPIError as e:
160 exit('/zones.get %d %s - api call failed' % (e, e))
161 except Exception as e:
162 exit('/zones.get - %s - api call failed' % (e))
163
164 if len(zones) == 0:
165 exit('No zones found')
166
167 # extract the zone_id which is needed to process that zone
168 zone = zones[0]
169 zone_id = zone['id']
170
171 # request the DNS records from that zone
172 try:
173 dns_records = cf.zones.dns_records.get(zone_id)
174 except CloudFlare.exceptions.CloudFlareAPIError as e:
175 exit('/zones/dns_records.get %d %s - api call failed' % (e, e))
176
177 # print the results - first the zone name
178 print zone_id, zone_name
179
180 # then all the DNS records for that zone
181 for dns_record in dns_records:
182 r_name = dns_record['name']
183 r_type = dns_record['type']
184 r_value = dns_record['content']
185 r_id = dns_record['id']
186 print '\t', r_id, r_name, r_type, r_value
187
188 exit(0)
189
190 if __name__ == '__main__':
191 main()
192
193Providing Cloudflare Username and API Key
194-----------------------------------------
195
196When you create a **CloudFlare** class you can pass up to four
197parameters.
198
199- API Token or API Key
200- Account email (only if an API Key is being used)
201- Optional Origin-CA Certificate Token
202- Optional Debug flag (True/False)
203- Optional Profile name (the default is ``Cloudflare``)
204
205.. code:: python
206
207 import CloudFlare
208
209 # A minimal call - reading values from environment variables or configuration file
210 cf = CloudFlare.CloudFlare()
211
212 # A minimal call with debug enabled
213 cf = CloudFlare.CloudFlare(debug=True)
214
215 # An authenticated call using an API Token (note the missing email)
216 cf = CloudFlare.CloudFlare(token='00000000000000000000000000000000')
217
218 # An authenticated call using an API Key
219 cf = CloudFlare.CloudFlare(email='user@example.com', token='00000000000000000000000000000000')
220
221 # An authenticated call using an API Key and CA-Origin info
222 cf = CloudFlare.CloudFlare(email='user@example.com', token='00000000000000000000000000000000', certtoken='v1.0-...')
223
224 # An authenticated call using using a stored profile (see below)
225 cf = CloudFlare.CloudFlare(profile="CompanyX"))
226
227If the account email and API key are not passed when you create the
228class, then they are retrieved from either the users exported shell
229environment variables or the .cloudflare.cfg or ~/.cloudflare.cfg or
230~/.cloudflare/cloudflare.cfg files, in that order.
231
232If you're using an API Token, any ``cloudflare.cfg`` file must either
233not contain an ``email`` attribute or be a zero length string and the
234``CF_API_EMAIL`` environment variable must be unset or be a zero length
235string, otherwise the token will be treated as a key and will throw an
236error.
237
238There is one call that presently doesn't need any email or token
239certification (the */ips* call); hence you can test without any values
240saved away.
241
242Using shell environment variables
243~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
244
245.. code:: bash
246
247 $ export CF_API_EMAIL='user@example.com' # Do not set if using an API Token
248 $ export CF_API_KEY='00000000000000000000000000000000'
249 $ export CF_API_CERTKEY='v1.0-...'
250 $
251
252These are optional environment variables; however, they do override the
253values set within a configuration file.
254
255Using configuration file to store email and keys
256~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
257
258.. code:: bash
259
260 $ cat ~/.cloudflare/cloudflare.cfg
261 [CloudFlare]
262 email = user@example.com # Do not set if using an API Token
263 token = 00000000000000000000000000000000
264 certtoken = v1.0-...
265 extras =
266 $
267
268More than one profile can be stored within that file. Here's an example
269for a work and home setup (in this example work has an API Token and
270home uses email/token).
271
272.. code:: bash
273
274 $ cat ~/.cloudflare/cloudflare.cfg
275 [Work]
276 token = 00000000000000000000000000000000
277 [Home]
278 email = home@example.com
279 token = 00000000000000000000000000000000
280 $
281
282To select a profile, use the ``--profile profile-name`` option for
283``cli4`` command or use ``profile="profile-name"`` in the library call.
284
285.. code:: bash
286
287 $ cli4 --profile Work /zones | jq '.[]|.name' | wc -l
288 13
289 $
290
291 $ cli4 --profile Home /zones | jq '.[]|.name' | wc -l
292 1
293 $
294
295Here is the same in code.
296
297.. code:: python
298
299 #!/usr/bin/env python
300
301 import CloudFlare
302
303 def main():
304 cf = CloudFlare.CloudFlare(profile="Work")
305 ...
306
307Advanced use of configuration file for authentication based on method
308~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
309
310The configuration file can have values that are both generic and
311specific to the method. Here's an example where a project has a
312different API Token for reading and writing values.
313
314.. code:: bash
315
316 $ cat ~/.cloudflare/cloudflare.cfg
317 [Work]
318 token = 0000000000000000000000000000000000000000
319 token.get = 0123456789012345678901234567890123456789
320 $
321
322When a GET call is processed then the second token is used. For all
323other calls the first token is used. Here's a more explict verion of
324that config:
325
326.. code:: bash
327
328 $ cat ~/.cloudflare/cloudflare.cfg
329 [Work]
330 token.delete = 0000000000000000000000000000000000000000
331 token.get = 0123456789012345678901234567890123456789
332 token.patch = 0000000000000000000000000000000000000000
333 token.post = 0000000000000000000000000000000000000000
334 token.put = 0000000000000000000000000000000000000000
335 $
336
337This can be used with email values also.
338
339About /certificates and certtoken
340~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
341
342The *CF\_API\_CERTKEY* or *certtoken* values are used for the Origin-CA
343*/certificates* API calls. You can leave *certtoken* in the
344configuration with a blank value (or omit the option variable fully).
345
346The *extras* values are used when adding API calls outside of the core
347codebase. Technically, this is only useful for internal testing within
348Cloudflare. You can leave *extras* in the configuration with a blank
349value (or omit the option variable fully).
350
351Exceptions and return values
352----------------------------
353
354Response data
355~~~~~~~~~~~~~
356
357The response is build from the JSON in the API call. It contains the
358**results** values; but does not contain the paging values.
359
360You can return all the paging values by calling the class with raw=True.
361Here's an example without paging.
362
363.. code:: python
364
365 #!/usr/bin/env python
366
367 import json
368 import CloudFlare
369
370 def main():
371 cf = CloudFlare.CloudFlare()
372 zones = cf.zones.get(params={'per_page':5})
373 print len(zones)
374
375 if __name__ == '__main__':
376 main()
377
378The results are as follows.
379
380::
381
382 5
383
384When you add the raw option; the APIs full structure is returned. This
385means the paging values can be seen.
386
387.. code:: python
388
389 #!/usr/bin/env python
390
391 import json
392 import CloudFlare
393
394 def main():
395 cf = CloudFlare.CloudFlare(raw=True)
396 zones = cf.zones.get(params={'per_page':5})
397 print zones.length()
398 print json.dumps(zones, indent=4, sort_keys=True)
399
400 if __name__ == '__main__':
401 main()
402
403This produces.
404
405::
406
407 5
408 {
409 "result": [
410 ...
411 ],
412 "result_info": {
413 "count": 5,
414 "page": 1,
415 "per_page": 5,
416 "total_count": 31,
417 "total_pages": 7
418 }
419 }
420
421A full example of paging is provided below.
422
423Exceptions
424~~~~~~~~~~
425
426The library will raise **CloudFlareAPIError** when the API call fails.
427The exception returns both an integer and textual message in one value.
428
429.. code:: python
430
431 import CloudFlare
432
433 ...
434 try
435 r = ...
436 except CloudFlare.exceptions.CloudFlareAPIError as e:
437 exit('api error: %d %s' % (e, e))
438 ...
439
440The other raised response is **CloudFlareInternalError** which can
441happen when calling an invalid method.
442
443In some cases more than one error is returned. In this case the return
444value **e** is also an array. You can iterate over that array to see the
445additional error.
446
447.. code:: python
448
449 import sys
450 import CloudFlare
451
452 ...
453 try
454 r = ...
455 except CloudFlare.exceptions.CloudFlareAPIError as e:
456 if len(e) > 0:
457 sys.stderr.write('api error - more than one error value returned!\n')
458 for x in e:
459 sys.stderr.write('api error: %d %s\n' % (x, x))
460 exit('api error: %d %s' % (e, e))
461 ...
462
463Exception examples
464~~~~~~~~~~~~~~~~~~
465
466Here's examples using the CLI command cli4 of the responses passed back
467in exceptions.
468
469First a simple get with a clean (non-error) response.
470
471::
472
473 $ cli4 /zones/:example.com/dns_records | jq -c '.[]|{"name":.name,"type":.type,"content":.content}'
474 {"name":"example.com","type":"MX","content":"something.example.com"}
475 {"name":"something.example.com","type":"A","content":"10.10.10.10"}
476 $
477
478Next a simple/single error response. This is simulated by providing
479incorrect authentication information.
480
481::
482
483 $ CF_API_EMAIL='someone@example.com' cli4 /zones/
484 cli4: /zones - 9103 Unknown X-Auth-Key or X-Auth-Email
485 $
486
487More than one call can be done on the same command line. In this mode,
488the connection is preserved between calls.
489
490::
491
492 $ cli4 /user/organizations /user/invites
493 ...
494 $
495
496Note that the output is presently two JSON structures one after the
497other - so less useful that you may think.
498
499Finally, a command that provides more than one error response. This is
500simulated by passing an invalid IPv4 address to a DNS record creation.
501
502::
503
504 $ cli4 --post name='foo' type=A content="1" /zones/:example.com/dns_records
505 cli4: /zones/:example.com/dns_records - 9005 Content for A record is invalid. Must be a valid IPv4 address
506 cli4: /zones/:example.com/dns_records - 1004 DNS Validation Error
507 $
508
509Included example code
510---------------------
511
512The
513`examples <https://github.com/cloudflare/python-cloudflare/tree/master/examples>`__
514folder contains many examples in both simple and verbose formats.
515
516A DNS zone code example
517-----------------------
518
519.. code:: python
520
521 #!/usr/bin/env python
522
523 import sys
524 import CloudFlare
525
526 def main():
527 zone_name = sys.argv[1]
528 cf = CloudFlare.CloudFlare()
529 zone_info = cf.zones.post(data={'jump_start':False, 'name': zone_name})
530 zone_id = zone_info['id']
531
532 dns_records = [
533 {'name':'foo', 'type':'AAAA', 'content':'2001:d8b::1'},
534 {'name':'foo', 'type':'A', 'content':'192.168.0.1'},
535 {'name':'duh', 'type':'A', 'content':'10.0.0.1', 'ttl':120},
536 {'name':'bar', 'type':'CNAME', 'content':'foo'},
537 {'name':'shakespeare', 'type':'TXT', 'content':"What's in a name? That which we call a rose by any other name ..."}
538 ]
539
540 for dns_record in dns_records:
541 r = cf.zones.dns_records.post(zone_id, data=dns_record)
542 exit(0)
543
544 if __name__ == '__main__':
545 main()
546
547A DNS zone delete code example (be careful)
548-------------------------------------------
549
550.. code:: python
551
552 #!/usr/bin/env python
553
554 import sys
555 import CloudFlare
556
557 def main():
558 zone_name = sys.argv[1]
559 cf = CloudFlare.CloudFlare()
560 zone_info = cf.zones.get(params={'name': zone_name})
561 zone_id = zone_info['id']
562
563 dns_name = sys.argv[2]
564 dns_records = cf.zones.dns_records.get(zone_id, params={'name':dns_name + '.' + zone_name})
565 for dns_record in dns_records:
566 dns_record_id = dns_record['id']
567 r = cf.zones.dns_records.delete(zone_id, dns_record_id)
568 exit(0)
569
570 if __name__ == '__main__':
571 main()
572
573CLI
574---
575
576All API calls can be called from the command line. The command will
577convert domain names prefixed with a colon (``:``) into
578zone\_identifiers: e.g. to view ``example.com`` you must use
579``cli4 /zones/:example.com`` (the zone ID cannot be used).
580
581.. code:: bash
582
583 $ cli4 [-V|--version] [-h|--help] [-v|--verbose] [-q|--quiet] [-j|--json] [-y|--yaml] [-r|--raw] [-d|--dump] [--get|--patch|--post|--put|--delete] [item=value ...] /command...
584
585CLI parameters for POST/PUT/PATCH
586~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
587
588For API calls that need to pass data or parameters there is various
589formats to use.
590
591The simplest form is ``item=value``. This passes the value as a string
592within the APIs JSON data.
593
594If you need a numeric value passed then **==** can be used to force the
595value to be treated as a numeric value within the APIs JSON data. For
596example: ``item==value``.
597
598if you need to pass a list of items; then **[]** can be used. For
599example:
600
601::
602
603 pool_id1="11111111111111111111111111111111"
604 pool_id2="22222222222222222222222222222222"
605 pool_id3="33333333333333333333333333333333"
606 cli4 --post global_pools="[ ${pool_id1}, ${pool_id2}, ${pool_id3} ]" region_pools="[ ]" /user/load_balancers/maps
607
608Data or parameters can be either named or unnamed. It can not be both.
609Named is the majority format; as described above. Unnamed parameters
610simply don't have anything before the **=** sign, as in ``=value``. This
611format is presently only used by the Cloudflare Load Balancer API calls.
612For example:
613
614::
615
616 cli4 --put ="00000000000000000000000000000000" /user/load_balancers/maps/:00000000000000000000000000000000/region/:WNAM
617
618Data can also be uploaded from file contents. Using the
619``item=@filename`` format will open the file and the contents uploaded
620in the POST.
621
622CLI output
623~~~~~~~~~~
624
625The output from the CLI command is in JSON or YAML format (and human
626readable). This is controled by the **--yaml** or **--json** flags (JSON
627is the default).
628
629Simple CLI examples
630~~~~~~~~~~~~~~~~~~~
631
632- ``cli4 /user/billing/profile``
633- ``cli4 /user/invites``
634
635- ``cli4 /zones/:example.com``
636- ``cli4 /zones/:example.com/dnssec``
637- ``cli4 /zones/:example.com/settings/ipv6``
638- ``cli4 --put /zones/:example.com/activation_check``
639- ``cli4 /zones/:example.com/keyless_certificates``
640
641- ``cli4 /zones/:example.com/analytics/dashboard``
642
643More complex CLI examples
644~~~~~~~~~~~~~~~~~~~~~~~~~
645
646Here is the creation of a DNS entry, followed by a listing of that entry
647and then the deletion of that entry.
648
649.. code:: bash
650
651 $ $ cli4 --post name="test" type="A" content="10.0.0.1" /zones/:example.com/dns_records
652 {
653 "id": "00000000000000000000000000000000",
654 "name": "test.example.com",
655 "type": "A",
656 "content": "10.0.0.1",
657 ...
658 }
659 $
660
661 $ cli4 /zones/:example.com/dns_records/:test.example.com | jq '{"id":.id,"name":.name,"type":.type,"content":.content}'
662 {
663 "id": "00000000000000000000000000000000",
664 "name": "test.example.com",
665 "type": "A",
666 "content": "10.0.0.1"
667 }
668
669 $ cli4 --delete /zones/:example.com/dns_records/:test.example.com | jq -c .
670 {"id":"00000000000000000000000000000000"}
671 $
672
673There's the ability to handle dns entries with multiple values. This
674produces more than one API call within the command.
675
676::
677
678 $ cli4 /zones/:example.com/dns_records/:test.example.com | jq -c '.[]|{"id":.id,"name":.name,"type":.type,"content":.content}'
679 {"id":"00000000000000000000000000000000","name":"test.example.com","type":"A","content":"192.168.0.1"}
680 {"id":"00000000000000000000000000000000","name":"test.example.com","type":"AAAA","content":"2001:d8b::1"}
681 $
682
683Here are the cache purging commands.
684
685.. code:: bash
686
687 $ cli4 --delete purge_everything=true /zones/:example.com/purge_cache | jq -c .
688 {"id":"00000000000000000000000000000000"}
689 $
690
691 $ cli4 --delete files='[http://example.com/css/styles.css]' /zones/:example.com/purge_cache | jq -c .
692 {"id":"00000000000000000000000000000000"}
693 $
694
695 $ cli4 --delete files='[http://example.com/css/styles.css,http://example.com/js/script.js]' /zones/:example.com/purge_cache | jq -c .
696 {"id":"00000000000000000000000000000000"}
697 $
698
699 $ cli4 --delete tags='[tag1,tag2,tag3]' /zones/:example.com/purge_cache | jq -c .
700 cli4: /zones/:example.com/purge_cache - 1107 Only enterprise zones can purge by tag.
701 $
702
703A somewhat useful listing of available plans for a specific zone.
704
705.. code:: bash
706
707 $ cli4 /zones/:example.com/available_plans | jq -c '.[]|{"id":.id,"name":.name}'
708 {"id":"00000000000000000000000000000000","name":"Pro Website"}
709 {"id":"00000000000000000000000000000000","name":"Business Website"}
710 {"id":"00000000000000000000000000000000","name":"Enterprise Website"}
711 {"id":"0feeeeeeeeeeeeeeeeeeeeeeeeeeeeee","name":"Free Website"}
712 $
713
714Cloudflare CA CLI examples
715~~~~~~~~~~~~~~~~~~~~~~~~~~
716
717Here's some Cloudflare CA examples. Note the need of the zone\_id=
718parameter with the basic **/certificates** call.
719
720.. code:: bash
721
722 $ cli4 /zones/:example.com | jq -c '.|{"id":.id,"name":.name}'
723 {"id":"12345678901234567890123456789012","name":"example.com"}
724 $
725
726 $ cli4 zone_id=12345678901234567890123456789012 /certificates | jq -c '.[]|{"id":.id,"expires_on":.expires_on,"hostnames":.hostnames,"certificate":.certificate}'
727 {"id":"123456789012345678901234567890123456789012345678","expires_on":"2032-01-29 22:36:00 +0000 UTC","hostnames":["*.example.com","example.com"],"certificate":"-----BEGIN CERTIFICATE-----\n ... "}
728 {"id":"123456789012345678901234567890123456789012345678","expires_on":"2032-01-28 23:23:00 +0000 UTC","hostnames":["*.example.com","example.com"],"certificate":"-----BEGIN CERTIFICATE-----\n ... "}
729 {"id":"123456789012345678901234567890123456789012345678","expires_on":"2032-01-28 23:20:00 +0000 UTC","hostnames":["*.example.com","example.com"],"certificate":"-----BEGIN CERTIFICATE-----\n ... "}
730 $
731
732A certificate can be viewed via a simple GET request.
733
734.. code:: bash
735
736 $ cli4 /certificates/:123456789012345678901234567890123456789012345678
737 {
738 "certificate": "-----BEGIN CERTIFICATE-----\n ... ",
739 "expires_on": "2032-01-29 22:36:00 +0000 UTC",
740 "hostnames": [
741 "*.example.com",
742 "example.com"
743 ],
744 "id": "123456789012345678901234567890123456789012345678",
745 "request_type": "origin-rsa"
746 }
747 $
748
749Creating a certificate. This is done with a **POST** request. Note the
750use of **==** in order to pass a decimal number (vs. string) in JSON.
751The CSR is not shown for simplicity sake.
752
753.. code:: bash
754
755 $ CSR=`cat example.com.csr`
756 $ cli4 --post hostnames='["example.com","*.example.com"]' requested_validity==365 request_type="origin-ecc" csr="$CSR" /certificates
757 {
758 "certificate": "-----BEGIN CERTIFICATE-----\n ... ",
759 "csr": "-----BEGIN CERTIFICATE REQUEST-----\n ... ",
760 "expires_on": "2018-09-27 21:47:00 +0000 UTC",
761 "hostnames": [
762 "*.example.com",
763 "example.com"
764 ],
765 "id": "123456789012345678901234567890123456789012345678",
766 "request_type": "origin-ecc",
767 "requested_validity": 365
768 }
769 $
770
771Deleting a certificate can be done with a **DELETE** call.
772
773.. code:: bash
774
775 $ cli4 --delete /certificates/:123456789012345678901234567890123456789012345678
776 {
777 "id": "123456789012345678901234567890123456789012345678",
778 "revoked_at": "0000-00-00T00:00:00Z"
779 }
780 $
781
782Paging CLI examples
783~~~~~~~~~~~~~~~~~~~
784
785The **--raw** command provides access to the paging returned values. See
786the API documentation for all the info. Here's an example of how to page
787thru a list of zones (it's included in the examples folder as
788**example\_paging\_thru\_zones.sh**).
789
790.. code:: bash
791
792 :
793 tmp=/tmp/$$_
794 trap "rm ${tmp}; exit 0" 0 1 2 15
795 PAGE=0
796 while true
797 do
798 cli4 --raw per_page=5 page=${PAGE} /zones > ${tmp}
799 domains=`jq -c '.|.result|.[]|.name' < ${tmp} | tr -d '"'`
800 result_info=`jq -c '.|.result_info' < ${tmp}`
801 COUNT=` echo "${result_info}" | jq .count`
802 PAGE=` echo "${result_info}" | jq .page`
803 PER_PAGE=` echo "${result_info}" | jq .per_page`
804 TOTAL_COUNT=`echo "${result_info}" | jq .total_count`
805 TOTAL_PAGES=`echo "${result_info}" | jq .total_pages`
806 echo COUNT=${COUNT} PAGE=${PAGE} PER_PAGE=${PER_PAGE} TOTAL_COUNT=${TOTAL_COUNT} TOTAL_PAGES=${TOTAL_PAGES} -- ${domains}
807 if [ "${PAGE}" == "${TOTAL_PAGES}" ]
808 then
809 ## last section
810 break
811 fi
812 # grab the next page
813 PAGE=`expr ${PAGE} + 1`
814 done
815
816It produces the following results.
817
818::
819
820 COUNT=5 PAGE=1 PER_PAGE=5 TOTAL_COUNT=31 TOTAL_PAGES=7 -- accumsan.example auctor.example consectetur.example dapibus.example elementum.example
821 COUNT=5 PAGE=2 PER_PAGE=5 TOTAL_COUNT=31 TOTAL_PAGES=7 -- felis.example iaculis.example ipsum.example justo.example lacus.example
822 COUNT=5 PAGE=3 PER_PAGE=5 TOTAL_COUNT=31 TOTAL_PAGES=7 -- lectus.example lobortis.example maximus.example morbi.example pharetra.example
823 COUNT=5 PAGE=4 PER_PAGE=5 TOTAL_COUNT=31 TOTAL_PAGES=7 -- porttitor.example potenti.example pretium.example purus.example quisque.example
824 COUNT=5 PAGE=5 PER_PAGE=5 TOTAL_COUNT=31 TOTAL_PAGES=7 -- sagittis.example semper.example sollicitudin.example suspendisse.example tortor.example
825 COUNT=1 PAGE=7 PER_PAGE=5 TOTAL_COUNT=31 TOTAL_PAGES=7 -- varius.example vehicula.example velit.example velit.example vitae.example
826 COUNT=5 PAGE=6 PER_PAGE=5 TOTAL_COUNT=31 TOTAL_PAGES=7 -- vivamus.example
827
828Paging thru lists (using cursors)
829~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
830
831Some API calls use cursors to read beyond the initally returned values.
832See the API page in order to see which API calls do this.
833
834::
835
836 $ ACCOUNT_ID="00000000000000000000000000000000"
837 $ LIST_ID="00000000000000000000000000000000"
838 $
839 $ cli4 --raw /accounts/::${ACCOUNT_ID}/rules/lists/::${LIST_ID}/items > /tmp/page1.json
840 $ after=`jq -r '.result_info.cursors.after' < /tmp/page1.json`
841 $ echo "after=$after"
842 after=Mxm4GVmKjYbFjy2VxMPipnJigm1M_s6lCS9ABR9wx-RM2A
843 $
844
845Once we have the ``after`` value, we can pass it along in order to read
846the next hunk of values. We finish when ``after`` returns as null (or
847isn't present).
848
849::
850
851 $ cli4 --raw cursor="$after" /accounts/::${ACCOUNT_ID}/rules/lists/::${LIST_ID}/items > /tmp/page2.json
852 $ after=`jq -r '.result_info.cursors.after' < /tmp/page2.json`
853 $ echo "after=$after"
854 after=null
855 $
856
857We can see the results now in two files.
858
859::
860
861 $ jq -c '.result[]' < /tmp/page1.json | wc -l
862 25
863 $
864
865 $ jq -c '.result[]' < /tmp/page2.json | wc -l
866 5
867 $
868
869 $ for f in /tmp/page?.json ; do jq -r '.result[]|.id,.ip,.comment' < $f | paste - - - ; done | column -s' ' -t
870 0fe44928258549feb47126a966fbf4a0 0.0.0.0 all zero
871 2e1e02120f5e466f8c0e26375e4cf4c8 1.0.0.1 Cloudflare DNS a
872 9ca5fd0ac6f54fdbb9dedd3fb72ce2da 1.1.1.1 Cloudflare DNS b
873 b3654987446743738c782f36ebe074f5 10.0.0.0/8 RFC1918 space
874 90bec8ce37d242faa2e27d1e78c1d8e2 103.21.244.0/22 Cloudflare IP
875 970a3c810cda41af9bef2c36a1892f7e 103.22.200.0/22 Cloudflare IP
876 3ec8516158bf4f3cac18210f611ee541 103.31.4.0/22 Cloudflare IP
877 ee9d268367204e6bb8e5e4c907f22de8 104.16.0.0/12 Cloudflare IP
878 93ae02eda9774c45840af367a02fe529 108.162.192.0/18 Cloudflare IP
879 62891ebf6db44aa494d79a6401af185e 131.0.72.0/22 Cloudflare IP
880 cac40cd940cc470582b8c912a8a12bea 141.101.64.0/18 Cloudflare IP
881 f6d5eacd81a2407f8e0d81caee21e7f8 162.158.0.0/15 Cloudflare IP
882 3d538dfc38ab471d9d3fe78332acfa4e 172.16.0.0/12 RFC1918 space
883 f353cb8f98424837ad35382a22b9debe 172.64.0.0/13 Cloudflare IP
884 78f3e1a0bafc41f88d4d40ad49a642e0 173.245.48.0/20 Cloudflare IP
885 c23a545475c54c32a7681c6b508d3e80 188.114.96.0/20 Cloudflare IP
886 f693237c9e294fe481221cbc2d7c20ef 190.93.240.0/20 Cloudflare IP
887 6d465ab3a0994c07827ebdcf8f34d977 192.168.0.0/16 RFC1918 space
888 1ad1e634b3664bac939086185c62faf7 197.234.240.0/22 Cloudflare IP
889 5d2968e7b3114d8e869a379d71c8ba86 198.41.128.0/17 Cloudflare IP
890 6a69de60b31448fa864f0a3ac5abe8d0 224.0.0.0/24 Multicast
891 30749cce89af4ab3a80e308294f46a46 240.0.0.0/4 Class E
892 2b32c67ea4d044628abe39f28662d8f0 255.255.255.255 all ones
893 cc7cd828b2fb4bcfb9391c2d3ef8d068 2400:cb00::/32 Cloudflare IP
894 b30d4cbd7dcd48729e8ebeda552e48a8 2405:8100::/32 Cloudflare IP
895 49db60758c8344959c338a74afc9748a 2405:b500::/32 Cloudflare IP
896 96e9eca1923c40d5a84865145f5a5d6a 2606:4700::/32 Cloudflare IP
897 21bc52a26e10405d89b7180ddcf49302 2803:f800::/32 Cloudflare IP
898 ff78f842188e4b869eb5389ae9ab8f41 2a06:98c0::/29 Cloudflare IP
899 0880cdfc40b14f6fa0639522a728859d 2c0f:f248::/32 Cloudflare IP
900 $
901
902The ``result_info.cursors`` area also contains a ``before`` value for
903reverse scrolling.
904
905As with ``per_page`` scrolling, raw mode is used.
906
907DNSSEC CLI examples
908~~~~~~~~~~~~~~~~~~~
909
910.. code:: bash
911
912 $ cli4 /zones/:example.com/dnssec | jq -c '{"status":.status}'
913 {"status":"disabled"}
914 $
915
916 $ cli4 --patch status=active /zones/:example.com/dnssec | jq -c '{"status":.status}'
917 {"status":"pending"}
918 $
919
920 $ cli4 /zones/:example.com/dnssec
921 {
922 "algorithm": "13",
923 "digest": "41600621c65065b09230ebc9556ced937eb7fd86e31635d0025326ccf09a7194",
924 "digest_algorithm": "SHA256",
925 "digest_type": "2",
926 "ds": "example.com. 3600 IN DS 2371 13 2 41600621c65065b09230ebc9556ced937eb7fd86e31635d0025326ccf09a7194",
927 "flags": 257,
928 "key_tag": 2371,
929 "key_type": "ECDSAP256SHA256",
930 "modified_on": "2016-05-01T22:42:15.591158Z",
931 "public_key": "mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==",
932 "status": "pending"
933 }
934 $
935
936Zone file upload and download CLI examples (uses BIND format files)
937~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
938
939Refer to `Import DNS
940records <https://api.cloudflare.com/#dns-records-for-a-zone-import-dns-records>`__
941on API documentation for this feature.
942
943.. code:: bash
944
945 $ cat zone.txt
946 example.com. IN SOA somewhere.example.com. someone.example.com. (
947 2017010101
948 3H
949 15
950 1w
951 3h
952 )
953
954 record1.example.com. IN A 10.0.0.1
955 record2.example.com. IN AAAA 2001:d8b::2
956 record3.example.com. IN CNAME record1.example.com.
957 record4.example.com. IN TXT "some text"
958 $
959
960 $ cli4 --post file=@zone.txt /zones/:example.com/dns_records/import
961 {
962 "recs_added": 4,
963 "total_records_parsed": 4
964 }
965 $
966
967The following is documented within the **Advanced** option of the DNS
968page within the Cloudflare portal.
969
970::
971
972 $ cli4 /zones/:example.com/dns_records/export | egrep -v '^;;|^$'
973 $ORIGIN .
974 @ 3600 IN SOA example.com. root.example.com. (
975 2025552311 ; serial
976 7200 ; refresh
977 3600 ; retry
978 86400 ; expire
979 3600) ; minimum
980 example.com. 300 IN NS REPLACE&ME$WITH^YOUR@NAMESERVER.
981 record4.example.com. 300 IN TXT "some text"
982 record3.example.com. 300 IN CNAME record1.example.com.
983 record1.example.com. 300 IN A 10.0.0.1
984 record2.example.com. 300 IN AAAA 2001:d8b::2
985 $
986
987The egrep is used for documentation brevity.
988
989This can also be done via Python code with the following example.
990
991::
992
993 #!/usr/bin/env python
994 import sys
995 import CloudFlare
996
997 def main():
998 zone_name = sys.argv[1]
999 cf = CloudFlare.CloudFlare()
1000
1001 zones = cf.zones.get(params={'name': zone_name})
1002 zone_id = zones[0]['id']
1003
1004 dns_records = cf.zones.dns_records.export.get(zone_id)
1005 for l in dns_records.splitlines():
1006 if len(l) == 0 or l[0] == ';':
1007 continue
1008 print l
1009 exit(0)
1010
1011 if __name__ == '__main__':
1012 main()
1013
1014Cloudflare Workers
1015~~~~~~~~~~~~~~~~~~
1016
1017Cloudflare Workers are described on the Cloudflare blog at
1018`here <https://blog.cloudflare.com/introducing-cloudflare-workers/>`__
1019and
1020`here <https://blog.cloudflare.com/code-everywhere-cloudflare-workers/>`__,
1021with the beta release announced
1022`here <https://blog.cloudflare.com/cloudflare-workers-is-now-on-open-beta/>`__.
1023
1024The Python libraries now support the Cloudflare Workers API calls. The
1025following javascript is lifted from https://cloudflareworkers.com/ and
1026slightly modified.
1027
1028::
1029
1030 $ cat modify-body.js
1031 addEventListener("fetch", event => {
1032 event.respondWith(fetchAndModify(event.request));
1033 });
1034
1035 async function fetchAndModify(request) {
1036 console.log("got a request:", request);
1037
1038 // Send the request on to the origin server.
1039 const response = await fetch(request);
1040
1041 // Read response body.
1042 const text = await response.text();
1043
1044 // Modify it.
1045 const modified = text.replace(
1046 "<body>",
1047 "<body style=\"background: #ff0;\">");
1048
1049 // Return modified response.
1050 return new Response(modified, {
1051 status: response.status,
1052 statusText: response.statusText,
1053 headers: response.headers
1054 });
1055 }
1056 $
1057
1058Here's the website with it's simple ``<body>`` statement
1059
1060::
1061
1062 $ curl -sS https://example.com/ | fgrep '<body'
1063 <body>
1064 $
1065
1066Now lets add the script. Looking above, you will see that it's simple
1067action is to modify the ``<body>`` statement and make the background
1068yellow.
1069
1070::
1071
1072 $ cli4 --put @- /zones/:example.com/workers/script < modify-body.js
1073 {
1074 "etag": "1234567890123456789012345678901234567890123456789012345678901234",
1075 "id": "example-com",
1076 "modified_on": "2018-02-15T00:00:00.000000Z",
1077 "script": "addEventListener(\"fetch\", event => {\n event.respondWith(fetchAndModify(event.request));\n});\n\nasync function fetchAndModify(request) {\n console.log(\"got a request:\", request);\n\n // Send the request on to the origin server.\n const response = await fetch(request);\n\n // Read response body.\n const text = await response.text();\n\n // Modify it.\n const modified = text.replace(\n \"<body>\",\n \"<body style=\\\"background: #ff0;\\\">\");\n\n // Return modified response.\n return new Response(modified, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers\n });\n}\n",
1078 "size": 603
1079 }
1080 $
1081
1082The following call checks that the script is associated with the zone.
1083In this case, it's the only script added by this user.
1084
1085::
1086
1087 $ cli4 /user/workers/scripts
1088 [
1089 {
1090 "created_on": "2018-02-15T00:00:00.000000Z",
1091 "etag": "1234567890123456789012345678901234567890123456789012345678901234",
1092 "id": "example-com",
1093 "modified_on": "2018-02-15T00:00:00.000000Z"
1094 }
1095 ]
1096 $
1097
1098Next step is to make sure a route is added for that script on that zone.
1099
1100::
1101
1102 $ cli4 --post pattern="example.com/*" script="example-com" /zones/:example.com/workers/routes
1103 {
1104 "id": "12345678901234567890123456789012"
1105 }
1106 $
1107
1108 $ cli4 /zones/:example.com/workers/routes
1109 [
1110 {
1111 "id": "12345678901234567890123456789012",
1112 "pattern": "example.com/*",
1113 "script": "example-com"
1114 }
1115 ]
1116 $
1117
1118With that script added to the zone and the route added, we can now see
1119the website has been modified because of the Cloudflare Worker.
1120
1121::
1122
1123 $ curl -sS https://example.com/ | fgrep '<body'
1124 <body style="background: #ff0;">
1125 $
1126
1127All this can be removed; hence bringing the website back to its initial
1128state.
1129
1130::
1131
1132 $ cli4 --delete /zones/:example.com/workers/script
1133 12345678901234567890123456789012
1134 $ cli4 --delete /zones/:example.com/workers/routes/:12345678901234567890123456789012
1135 true
1136 $
1137
1138 $ curl -sS https://example.com/ | fgrep '<body'
1139 <body>
1140 $
1141
1142Refer to the Cloudflare Workers API documentation for more information.
1143
1144Cloudflare GraphQL
1145------------------
1146
1147The GraphQL interface can be accessed via the command line or via
1148Python.
1149
1150::
1151
1152 query="""
1153 query {
1154 viewer {
1155 zones(filter: {zoneTag: "%s"} ) {
1156 httpRequests1dGroups(limit:40, filter:{date_lt: "%s", date_gt: "%s"}) {
1157 sum { countryMap { bytes, requests, clientCountryName } }
1158 dimensions { date }
1159 }
1160 }
1161 }
1162 }
1163 """ % (zone_id, date_before[0:10], date_after[0:10])
1164
1165 r = cf.graphql.post(data={'query':query})
1166
1167 httpRequests1dGroups = zone_info = r['data']['viewer']['zones'][0]['httpRequests1dGroups']
1168
1169See the `examples/example\_graphql.sh <examples/example_graphql.sh>`__
1170and `examples/example\_graphql.py <examples/example_graphql.py>`__ files
1171for working examples. Here is the working example of the shell version:
1172
1173::
1174
1175 $ examples/example_graphql.sh example.com
1176 2020-07-14T02:00:00Z 34880
1177 2020-07-14T03:00:00Z 18953
1178 2020-07-14T04:00:00Z 28700
1179 2020-07-14T05:00:00Z 2358
1180 2020-07-14T06:00:00Z 34905
1181 2020-07-14T07:00:00Z 779
1182 2020-07-14T08:00:00Z 35450
1183 2020-07-14T10:00:00Z 17803
1184 2020-07-14T11:00:00Z 32678
1185 2020-07-14T12:00:00Z 19947
1186 2020-07-14T13:00:00Z 4956
1187 2020-07-14T14:00:00Z 34585
1188 2020-07-14T15:00:00Z 3022
1189 2020-07-14T16:00:00Z 5224
1190 2020-07-14T18:00:00Z 79482
1191 2020-07-14T21:00:00Z 10609
1192 2020-07-14T22:00:00Z 5740
1193 2020-07-14T23:00:00Z 2545
1194 2020-07-15T01:00:00Z 10777
1195 $
1196
1197For more information on how to use GraphQL at Cloudflare, refer to the
1198`Cloudflare GraphQL Analytics
1199API <https://developers.cloudflare.com/analytics/graphql-api>`__. It
1200contains a full overview of Cloudflare's GraphQL features and keywords.
1201
1202Implemented API calls
1203---------------------
1204
1205The **--dump** argument to cli4 will produce a list of all the call
1206implemented within the library.
1207
1208.. code:: bash
1209
1210 $ cli4 --dump
1211 /certificates
1212 /ips
1213 /organizations
1214 ...
1215 /zones/ssl/analyze
1216 /zones/ssl/certificate_packs
1217 /zones/ssl/verification
1218 $
1219
1220Table of commands
1221~~~~~~~~~~~~~~~~~
1222
1223+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1224| ``GET`` | ``PUT`` | ``POST`` | ``PATCH`` | ``DELETE`` | API call |
1225+===========+===========+============+=============+==============+===============================================================+
1226| ``GET`` | | ``POST`` | | ``DELETE`` | /certificates |
1227+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1228| ``GET`` | | | | | /ips |
1229+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1230| ``GET`` | | | ``PATCH`` | | /organizations |
1231+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1232| ``GET`` | | ``POST`` | ``PATCH`` | ``DELETE`` | /organizations/:identifier/firewall/access\_rules/rules |
1233+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1234| | | | ``PATCH`` | | /organizations/:identifier/invite |
1235+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1236| ``GET`` | | ``POST`` | | ``DELETE`` | /organizations/:identifier/invites |
1237+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1238| ``GET`` | | | ``PATCH`` | ``DELETE`` | /organizations/:identifier/members |
1239+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1240| ``GET`` | | ``POST`` | ``PATCH`` | ``DELETE`` | /organizations/:identifier/railguns |
1241+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1242| ``GET`` | | | | | /organizations/:identifier/railguns/:identifier/zones |
1243+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1244| ``GET`` | | | | | /organizations/:identifier/roles |
1245+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1246| ``GET`` | | ``POST`` | ``PATCH`` | ``DELETE`` | /organizations/:identifier/virtual\_dns |
1247+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1248| ``GET`` | | ``POST`` | ``PATCH`` | ``DELETE`` | /railguns |
1249+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1250| ``GET`` | | | | | /railguns/:identifier/zones |
1251+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1252| ``GET`` | | | ``PATCH`` | | /user |
1253+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1254| ``GET`` | | | | | /user/billing/history |
1255+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1256| ``GET`` | | | | | /user/billing/profile |
1257+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1258| ``GET`` | | | | | /user/billing/subscriptions/apps |
1259+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1260| ``GET`` | | | | | /user/billing/subscriptions/zones |
1261+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1262| ``GET`` | | ``POST`` | ``PATCH`` | ``DELETE`` | /user/firewall/access\_rules/rules |
1263+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1264| ``GET`` | | | ``PATCH`` | | /user/invites |
1265+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1266| ``GET`` | | | | ``DELETE`` | /user/organizations |
1267+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1268| ``GET`` | | ``POST`` | ``PATCH`` | ``DELETE`` | /user/virtual\_dns |
1269+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1270| ``GET`` | | ``POST`` | ``PATCH`` | ``DELETE`` | /zones |
1271+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1272| | ``PUT`` | | | | /zones/:identifier/activation\_check |
1273+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1274| ``GET`` | | | | | /zones/:identifier/analytics/colos |
1275+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1276| ``GET`` | | | | | /zones/:identifier/analytics/dashboard |
1277+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1278| ``GET`` | | | | | /zones/:identifier/available\_plans |
1279+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1280| | ``PUT`` | | | | /zones/:identifier/custom\_certificates/prioritize |
1281+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1282| ``GET`` | | ``POST`` | ``PATCH`` | ``DELETE`` | /zones/:identifier/custom\_certificates |
1283+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1284| ``GET`` | ``PUT`` | | | | /zones/:identifier/custom\_pages |
1285+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1286| ``GET`` | ``PUT`` | ``POST`` | | ``DELETE`` | /zones/:identifier/dns\_records |
1287+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1288| ``GET`` | | | ``PATCH`` | | /zones/:identifier/firewall/waf/packages/:identifier/groups |
1289+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1290| ``GET`` | | | ``PATCH`` | | /zones/:identifier/firewall/waf/packages/:identifier/rules |
1291+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1292| ``GET`` | | | ``PATCH`` | | /zones/:identifier/firewall/waf/packages |
1293+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1294| ``GET`` | | ``POST`` | ``PATCH`` | ``DELETE`` | /zones/:identifier/firewall/access\_rules/rules |
1295+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1296| ``GET`` | | ``POST`` | ``PATCH`` | ``DELETE`` | /zones/:identifier/keyless\_certificates |
1297+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1298| ``GET`` | ``PUT`` | ``POST`` | ``PATCH`` | ``DELETE`` | /zones/:identifier/pagerules |
1299+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1300| | | | | ``DELETE`` | /zones/:identifier/purge\_cache |
1301+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1302| ``GET`` | | | | | /zones/:identifier/railguns/:identifier/diagnose |
1303+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1304| ``GET`` | | | ``PATCH`` | | /zones/:identifier/railguns |
1305+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1306| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings |
1307+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1308| ``GET`` | | | | | /zones/:identifier/settings/advanced\_ddos |
1309+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1310| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/always\_online |
1311+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1312| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/browser\_cache\_ttl |
1313+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1314| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/browser\_check |
1315+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1316| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/cache\_level |
1317+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1318| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/challenge\_ttl |
1319+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1320| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/development\_mode |
1321+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1322| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/email\_obfuscation |
1323+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1324| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/hotlink\_protection |
1325+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1326| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/ip\_geolocation |
1327+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1328| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/ipv6 |
1329+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1330| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/minify |
1331+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1332| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/mirage |
1333+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1334| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/mobile\_redirect |
1335+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1336| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/origin\_error\_page\_pass\_thru |
1337+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1338| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/polish |
1339+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1340| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/prefetch\_preload |
1341+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1342| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/response\_buffering |
1343+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1344| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/rocket\_loader |
1345+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1346| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/security\_header |
1347+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1348| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/security\_level |
1349+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1350| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/server\_side\_exclude |
1351+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1352| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/sort\_query\_string\_for\_cache |
1353+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1354| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/ssl |
1355+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1356| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/tls\_1\_2\_only |
1357+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1358| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/tls\_client\_auth |
1359+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1360| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/true\_client\_ip\_header |
1361+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1362| ``GET`` | | | ``PATCH`` | | /zones/:identifier/settings/waf |
1363+-----------+-----------+------------+-------------+--------------+---------------------------------------------------------------+
1364
1365Adding extra API calls manually
1366-------------------------------
1367
1368Extra API calls can be added via the configuration file
1369
1370.. code:: bash
1371
1372 $ cat ~/.cloudflare/cloudflare.cfg
1373 [CloudFlare]
1374 extras =
1375 /client/v4/command
1376 /client/v4/command/:command_identifier
1377 /client/v4/command/:command_identifier/settings
1378 $
1379
1380While it's easy to call anything within Cloudflare's API, it's not very
1381useful to add items in here as they will simply return API URL errors.
1382Technically, this is only useful for internal testing within Cloudflare.
1383
1384Issues
1385------
1386
1387The following error can be caused by an out of date SSL/TLS library
1388and/or out of date Python.
1389
1390::
1391
1392 /usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/util/ssl_.py:318: SNIMissingWarning: An HTTPS request has been made, but the SNI (Subject Name Indication) extension to TLS is not available on this platform. This may cause the server to present an incorrect TLS certificate, which can cause validation failures. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#snimissingwarning.
1393 SNIMissingWarning
1394 /usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/util/ssl_.py:122: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
1395 InsecurePlatformWarning
1396
1397The solution can be found
1398`here <https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning>`__
1399and/or
1400`here <http://stackoverflow.com/questions/35144550/how-to-install-cryptography-on-ubuntu>`__.
1401
1402Python 2.x vs 3.x support
1403-------------------------
1404
1405As of May/June 2016 the code is now tested against pylint. This was
1406required in order to move the codebase into Python 3.x. The motivation
1407for this came from `Danielle Madeley
1408(danni) <https://github.com/danni>`__.
1409
1410[STRIKEOUT:While the codebase has been edited to run on Python 3.x,
1411there's not been enough Python 3.x testing performed.] [STRIKEOUT:If you
1412can help in this regard; please contact the maintainers.]
1413
1414As of January 2020 the code is Python3 clean.
1415
1416As of January 2020 the code is shipped up to pypi with Python2 support
1417removed.
1418
1419As of January 2020 the code is Python3.8 clean. The new
1420``SyntaxWarning`` messages (i.e.
1421``SyntaxWarning: "is" with a literal. Did you mean "=="?``) meant minor
1422edits were needed.
1423
1424Credit
1425------
1426
1427This is based on work by `Felix Wong
1428(gnowxilef) <https://github.com/gnowxilef>`__ found
1429`here <https://github.com/cloudflare-api/python-cloudflare-v4>`__. It
1430has been seriously expanded upon.
1431
1432Changelog
1433---------
1434
1435An automatically generated CHANGELOG is provided
1436`here <CHANGELOG.md>`__.
1437
1438Copyright
1439---------
1440
1441Portions copyright `Felix Wong
1442(gnowxilef) <https://github.com/gnowxilef>`__ 2015 and Cloudflare 2016.
1443