Repository: softlayer/softlayer-python
Pull request #1151 (2019-06-10 15:40:05)

feature cdn network
Feature cdn network https://github.com/softlayer/softlayer-python/issues/1145.

C:\> slcli cdn

```
Usage: slcli cdn [OPTIONS] COMMAND [ARGS]...

  Content Delivery Network.

Options:
  -h, --help  Show this message and exit.

Commands:
  detail         Detail a CDN Account.
  list           List all CDN accounts.
  load           Cache one or more files on all edge nodes.
  origin-add     Create an origin path for an existing CDN mapping.
  origin-list    List origin path for an existing CDN mapping.
  origin-remove  Removes an origin path for an existing CDN mapping.
  purge          Creates a purge record and also initiates the purge call.
```
C:\>slcli cdn detail --help
```
Options:
  --last_days INTEGER  cdn overview last days less than 90 days, because it is the maximum e.g 7, 15, 30, 60, 89
  -h, --help           Show this message and exit.
```
C:\>slcli cdn detail --last_days 30 9934111111111
Output
```
:.................:.....................:
:            name : value               :
:.................:.....................:
:       unique_id : 993411111111     :
:        hostname : test.example.com    :
:        protocol : HTTP                :
:          origin : 10.10.10.1          :
:     origin_type : HOST_SERVER         :
:            path : /                   :
:        provider : akamai              :
:          status : CNAME_CONFIGURATION :
: total_bandwidth : 0.0 GB              :
:      total_hits : 0                   :
:       hit_radio : 0.0 %               :
:.................:.....................:
```
C:\>slcli cdn origin-list 993411111111111
output
```
:................:...............:...........:.........:
:      Path      :     Origin    : HTTP Port :  Status :
:................:...............:...........:.........:
:    /example    :   10.10.10.1  :     80    : RUNNING :
:   /example1    :   10.10.10.1  :     80    : RUNNING :
:   /example2    :   10.10.10.1  :     80    : RUNNING :
```
C:\>slcli cdn origin-add -H test.example.com -p 80 -o web -c include-all 99341111111111 10.10.10.1 /example/videos
Output
```
:...............:......................:
:          Item :                Value :
:...............:......................:
: CDN Unique ID :      9934111111111 :
:        Origin :           10.10.10.1 :
:   Origin Type :          HOST_SERVER :
:          Path :    /example/videos/* :
:          Port :                   80 :
: Configuration : General web delivery :
:        Status :              RUNNING :
:...............:......................:
```
C:\>slcli cdn origin-remove 993411111111111 /example/videos/*
Output
`Origin with path /example/videos/* has been deleted`




Patch:
diff --git a/SoftLayer/CLI/cdn/detail.py b/SoftLayer/CLI/cdn/detail.py
index 509db536..ef93d279 100644
--- a/SoftLayer/CLI/cdn/detail.py
+++ b/SoftLayer/CLI/cdn/detail.py
@@ -9,24 +9,38 @@
 
 
 @click.command()
-@click.argument('account_id')
+@click.argument('unique_id')
+@click.option('--history',
+              default=30, type=click.IntRange(1, 89),
+              help='Bandwidth, Hits, Ratio counted over history number of days ago. 89 is the maximum. ')
 @environment.pass_env
-def cli(env, account_id):
+def cli(env, unique_id, history):
     """Detail a CDN Account."""
 
     manager = SoftLayer.CDNManager(env.client)
-    account = manager.get_account(account_id)
+
+    cdn_mapping = manager.get_cdn(unique_id)
+    cdn_metrics = manager.get_usage_metrics(unique_id, history=history)
+
+    # usage metrics
+    total_bandwidth = "%s GB" % cdn_metrics['totals'][0]
+    total_hits = cdn_metrics['totals'][1]
+    hit_ratio = "%s %%" % cdn_metrics['totals'][2]
 
     table = formatting.KeyValueTable(['name', 'value'])
     table.align['name'] = 'r'
     table.align['value'] = 'l'
 
-    table.add_row(['id', account['id']])
-    table.add_row(['account_name', account['cdnAccountName']])
-    table.add_row(['type', account['cdnSolutionName']])
-    table.add_row(['status', account['status']['name']])
-    table.add_row(['created', account['createDate']])
-    table.add_row(['notes',
-                   account.get('cdnAccountNote', formatting.blank())])
+    table.add_row(['unique_id', cdn_mapping['uniqueId']])
+    table.add_row(['hostname', cdn_mapping['domain']])
+    table.add_row(['protocol', cdn_mapping['protocol']])
+    table.add_row(['origin', cdn_mapping['originHost']])
+    table.add_row(['origin_type', cdn_mapping['originType']])
+    table.add_row(['path', cdn_mapping['path']])
+    table.add_row(['provider', cdn_mapping['vendorName']])
+    table.add_row(['status', cdn_mapping['status']])
+    table.add_row(['total_bandwidth', total_bandwidth])
+    table.add_row(['total_hits', total_hits])
+    table.add_row(['hit_radio', hit_ratio])
 
     env.fout(table)
diff --git a/SoftLayer/CLI/cdn/list.py b/SoftLayer/CLI/cdn/list.py
index 2e1b0778..994a338b 100644
--- a/SoftLayer/CLI/cdn/list.py
+++ b/SoftLayer/CLI/cdn/list.py
@@ -11,32 +11,33 @@
 @click.command()
 @click.option('--sortby',
               help='Column to sort by',
-              type=click.Choice(['id',
-                                 'datacenter',
-                                 'host',
-                                 'cores',
-                                 'memory',
-                                 'primary_ip',
-                                 'backend_ip']))
+              type=click.Choice(['unique_id',
+                                 'domain',
+                                 'origin',
+                                 'vendor',
+                                 'cname',
+                                 'status']))
 @environment.pass_env
 def cli(env, sortby):
     """List all CDN accounts."""
 
     manager = SoftLayer.CDNManager(env.client)
-    accounts = manager.list_accounts()
+    accounts = manager.list_cdn()
 
-    table = formatting.Table(['id',
-                              'account_name',
-                              'type',
-                              'created',
-                              'notes'])
+    table = formatting.Table(['unique_id',
+                              'domain',
+                              'origin',
+                              'vendor',
+                              'cname',
+                              'status'])
     for account in accounts:
         table.add_row([
-            account['id'],
-            account['cdnAccountName'],
-            account['cdnSolutionName'],
-            account['createDate'],
-            account.get('cdnAccountNote', formatting.blank())
+            account['uniqueId'],
+            account['domain'],
+            account['originHost'],
+            account['vendorName'],
+            account['cname'],
+            account['status']
         ])
 
     table.sortby = sortby
diff --git a/SoftLayer/CLI/cdn/load.py b/SoftLayer/CLI/cdn/load.py
deleted file mode 100644
index 648f4f34..00000000
--- a/SoftLayer/CLI/cdn/load.py
+++ /dev/null
@@ -1,18 +0,0 @@
-"""Cache one or more files on all edge nodes."""
-# :license: MIT, see LICENSE for more details.
-
-import click
-
-import SoftLayer
-from SoftLayer.CLI import environment
-
-
-@click.command()
-@click.argument('account_id')
-@click.argument('content_url', nargs=-1)
-@environment.pass_env
-def cli(env, account_id, content_url):
-    """Cache one or more files on all edge nodes."""
-
-    manager = SoftLayer.CDNManager(env.client)
-    manager.load_content(account_id, content_url)
diff --git a/SoftLayer/CLI/cdn/origin_add.py b/SoftLayer/CLI/cdn/origin_add.py
index 51d789da..08790d9b 100644
--- a/SoftLayer/CLI/cdn/origin_add.py
+++ b/SoftLayer/CLI/cdn/origin_add.py
@@ -5,22 +5,82 @@
 
 import SoftLayer
 from SoftLayer.CLI import environment
-
-# pylint: disable=redefined-builtin
+from SoftLayer.CLI import exceptions
+from SoftLayer.CLI import formatting
 
 
 @click.command()
-@click.argument('account_id')
-@click.argument('content_url')
-@click.option('--type',
-              help='The media type for this mapping (http, flash, wm, ...)',
+@click.argument('unique_id')
+@click.argument('origin')
+@click.argument('path')
+@click.option('--origin-type', '-t',
+              type=click.Choice(['server', 'storage']),
+              help='The origin type.',
+              default='server',
+              show_default=True)
+@click.option('--header', '-H',
+              type=click.STRING,
+              help='The host header to communicate with the origin.')
+@click.option('--bucket-name', '-b',
+              type=click.STRING,
+              help="The name of the available resource [required if --origin-type=storage]")
+@click.option('--port', '-p',
+              type=click.INT,
+              help="The http port number.",
+              default=80,
+              show_default=True)
+@click.option('--protocol', '-P',
+              type=click.STRING,
+              help="The protocol used by the origin.",
               default='http',
               show_default=True)
-@click.option('--cname',
-              help='An optional CNAME to attach to the mapping')
+@click.option('--optimize-for', '-o',
+              type=click.Choice(['web', 'video', 'file']),
+              help="Performance configuration",
+              default='web',
+              show_default=True)
+@click.option('--extensions', '-e',
+              type=click.STRING,
+              help="File extensions that can be stored in the CDN, example: 'jpg, png, pdf'")
+@click.option('--cache-query', '-c',
+              type=click.STRING,
+              help="Cache query rules with the following formats:\n"
+                   "'ignore-all', 'include: <query-names>', 'ignore: <query-names>'",
+              default="include-all",
+              show_default=True)
 @environment.pass_env
-def cli(env, account_id, content_url, type, cname):
-    """Create an origin pull mapping."""
+def cli(env, unique_id, origin, path, origin_type, header,
+        bucket_name, port, protocol, optimize_for, extensions, cache_query):
+    """Create an origin path for an existing CDN mapping.
+
+    For more information see the following documentation: \n
+    https://cloud.ibm.com/docs/infrastructure/CDN?topic=CDN-manage-your-cdn#adding-origin-path-details
+    """
 
     manager = SoftLayer.CDNManager(env.client)
-    manager.add_origin(account_id, type, content_url, cname)
+
+    if origin_type == 'storage' and not bucket_name:
+        raise exceptions.ArgumentError('[-b | --bucket-name] is required when [-t | --origin-type] is "storage"')
+
+    result = manager.add_origin(unique_id, origin, path, origin_type=origin_type,
+                                header=header, port=port, protocol=protocol,
+                                bucket_name=bucket_name, file_extensions=extensions,
+                                optimize_for=optimize_for, cache_query=cache_query)
+
+    table = formatting.Table(['Item', 'Value'])
+    table.align['Item'] = 'r'
+    table.align['Value'] = 'r'
+
+    table.add_row(['CDN Unique ID', result['mappingUniqueId']])
+
+    if origin_type == 'storage':
+        table.add_row(['Bucket Name', result['bucketName']])
+
+    table.add_row(['Origin', result['origin']])
+    table.add_row(['Origin Type', result['originType']])
+    table.add_row(['Path', result['path']])
+    table.add_row(['Port', result['httpPort']])
+    table.add_row(['Configuration', result['performanceConfiguration']])
+    table.add_row(['Status', result['status']])
+
+    env.fout(table)
diff --git a/SoftLayer/CLI/cdn/origin_list.py b/SoftLayer/CLI/cdn/origin_list.py
index 1867a9cd..208c26f6 100644
--- a/SoftLayer/CLI/cdn/origin_list.py
+++ b/SoftLayer/CLI/cdn/origin_list.py
@@ -9,20 +9,20 @@
 
 
 @click.command()
-@click.argument('account_id')
+@click.argument('unique_id')
 @environment.pass_env
-def cli(env, account_id):
-    """List origin pull mappings."""
+def cli(env, unique_id):
+    """List origin path for an existing CDN mapping."""
 
     manager = SoftLayer.CDNManager(env.client)
-    origins = manager.get_origins(account_id)
+    origins = manager.get_origins(unique_id)
 
-    table = formatting.Table(['id', 'media_type', 'cname', 'origin_url'])
+    table = formatting.Table(['Path', 'Origin', 'HTTP Port', 'Status'])
 
     for origin in origins:
-        table.add_row([origin['id'],
-                       origin['mediaType'],
-                       origin.get('cname', formatting.blank()),
-                       origin['originUrl']])
+        table.add_row([origin['path'],
+                       origin['origin'],
+                       origin['httpPort'],
+                       origin['status']])
 
     env.fout(table)
diff --git a/SoftLayer/CLI/cdn/origin_remove.py b/SoftLayer/CLI/cdn/origin_remove.py
index 2b8855ed..4e417238 100644
--- a/SoftLayer/CLI/cdn/origin_remove.py
+++ b/SoftLayer/CLI/cdn/origin_remove.py
@@ -8,11 +8,13 @@
 
 
 @click.command()
-@click.argument('account_id')
-@click.argument('origin_id')
+@click.argument('unique_id')
+@click.argument('origin_path')
 @environment.pass_env
-def cli(env, account_id, origin_id):
-    """Remove an origin pull mapping."""
+def cli(env, unique_id, origin_path):
+    """Removes an origin path for an existing CDN mapping."""
 
     manager = SoftLayer.CDNManager(env.client)
-    manager.remove_origin(account_id, origin_id)
+    manager.remove_origin(unique_id, origin_path)
+
+    click.secho("Origin with path %s has been deleted" % origin_path, fg='green')
diff --git a/SoftLayer/CLI/cdn/purge.py b/SoftLayer/CLI/cdn/purge.py
index bcf05506..26bff1dd 100644
--- a/SoftLayer/CLI/cdn/purge.py
+++ b/SoftLayer/CLI/cdn/purge.py
@@ -9,26 +9,30 @@
 
 
 @click.command()
-@click.argument('account_id')
-@click.argument('content_url', nargs=-1)
+@click.argument('unique_id')
+@click.argument('path')
 @environment.pass_env
-def cli(env, account_id, content_url):
-    """Purge cached files from all edge nodes.
+def cli(env, unique_id, path):
+    """Creates a purge record and also initiates the purge call.
 
-    Examples:
-         slcli cdn purge 97794 http://example.com/cdn/file.txt
-         slcli cdn purge 97794 http://example.com/cdn/file.txt https://dal01.example.softlayer.net/image.png
+        Example:
+             slcli cdn purge 9779455 /article/file.txt
+
+        For more information see the following documentation: \n
+        https://cloud.ibm.com/docs/infrastructure/CDN?topic=CDN-manage-your-cdn#purging-cached-content
     """
 
     manager = SoftLayer.CDNManager(env.client)
-    content_list = manager.purge_content(account_id, content_url)
+    result = manager.purge_content(unique_id, path)
 
-    table = formatting.Table(['url', 'status'])
+    table = formatting.Table(['Date', 'Path', 'Saved', 'Status'])
 
-    for content in content_list:
+    for data in result:
         table.add_row([
-            content['url'],
-            content['statusCode']
+            data['date'],
+            data['path'],
+            data['saved'],
+            data['status']
         ])
 
     env.fout(table)
diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py
index 62c1baa4..5524b694 100644
--- a/SoftLayer/CLI/routes.py
+++ b/SoftLayer/CLI/routes.py
@@ -54,7 +54,6 @@
     ('cdn', 'SoftLayer.CLI.cdn'),
     ('cdn:detail', 'SoftLayer.CLI.cdn.detail:cli'),
     ('cdn:list', 'SoftLayer.CLI.cdn.list:cli'),
-    ('cdn:load', 'SoftLayer.CLI.cdn.load:cli'),
     ('cdn:origin-add', 'SoftLayer.CLI.cdn.origin_add:cli'),
     ('cdn:origin-list', 'SoftLayer.CLI.cdn.origin_list:cli'),
     ('cdn:origin-remove', 'SoftLayer.CLI.cdn.origin_remove:cli'),
diff --git a/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Configuration_Cache_Purge.py b/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Configuration_Cache_Purge.py
new file mode 100644
index 00000000..cd0d2810
--- /dev/null
+++ b/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Configuration_Cache_Purge.py
@@ -0,0 +1,1 @@
+createPurge = []
diff --git a/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Configuration_Mapping.py b/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Configuration_Mapping.py
new file mode 100644
index 00000000..51950b91
--- /dev/null
+++ b/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Configuration_Mapping.py
@@ -0,0 +1,31 @@
+listDomainMappings = [
+    {
+        "cname": "cdnakauuiet7s6u6.cdnedge.bluemix.net",
+        "domain": "test.example.com",
+        "header": "test.example.com",
+        "httpPort": 80,
+        "originHost": "1.1.1.1",
+        "originType": "HOST_SERVER",
+        "path": "/",
+        "protocol": "HTTP",
+        "status": "CNAME_CONFIGURATION",
+        "uniqueId": "9934111111111",
+        "vendorName": "akamai"
+    }
+]
+
+listDomainMappingByUniqueId = [
+    {
+        "cname": "cdnakauuiet7s6u6.cdnedge.bluemix.net",
+        "domain": "test.example.com",
+        "header": "test.example.com",
+        "httpPort": 80,
+        "originHost": "1.1.1.1",
+        "originType": "HOST_SERVER",
+        "path": "/",
+        "protocol": "HTTP",
+        "status": "CNAME_CONFIGURATION",
+        "uniqueId": "9934111111111",
+        "vendorName": "akamai"
+    }
+]
diff --git a/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Configuration_Mapping_Path.py b/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Configuration_Mapping_Path.py
new file mode 100644
index 00000000..59705f7b
--- /dev/null
+++ b/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Configuration_Mapping_Path.py
@@ -0,0 +1,37 @@
+listOriginPath = [
+    {
+        "header": "test.example.com",
+        "httpPort": 80,
+        "mappingUniqueId": "993419389425697",
+        "origin": "10.10.10.1",
+        "originType": "HOST_SERVER",
+        "path": "/example",
+        "status": "RUNNING"
+    },
+    {
+        "header": "test.example.com",
+        "httpPort": 80,
+        "mappingUniqueId": "993419389425697",
+        "origin": "10.10.10.1",
+        "originType": "HOST_SERVER",
+        "path": "/example1",
+        "status": "RUNNING"
+    }
+]
+
+createOriginPath = [
+    {
+        "header": "test.example.com",
+        "httpPort": 80,
+        "mappingUniqueId": "993419389425697",
+        "origin": "10.10.10.1",
+        "originType": "HOST_SERVER",
+        "path": "/example",
+        "status": "RUNNING",
+        "bucketName": "test-bucket",
+        'fileExtension': 'jpg',
+        "performanceConfiguration": "General web delivery"
+    }
+]
+
+deleteOriginPath = "Origin with path /example/videos/* has been deleted"
diff --git a/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Metrics.py b/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Metrics.py
new file mode 100644
index 00000000..6b6aab5b
--- /dev/null
+++ b/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Metrics.py
@@ -0,0 +1,15 @@
+getMappingUsageMetrics = [
+    {
+        "names": [
+            "TotalBandwidth",
+            "TotalHits",
+            "HitRatio"
+        ],
+        "totals": [
+            "0.0",
+            "0",
+            "0.0"
+        ],
+        "type": "TOTALS"
+    }
+]
diff --git a/SoftLayer/fixtures/SoftLayer_Network_ContentDelivery_Account.py b/SoftLayer/fixtures/SoftLayer_Network_ContentDelivery_Account.py
deleted file mode 100644
index 643df9c1..00000000
--- a/SoftLayer/fixtures/SoftLayer_Network_ContentDelivery_Account.py
+++ /dev/null
@@ -1,41 +0,0 @@
-getObject = {
-    "cdnAccountName": "1234a",
-    "providerPortalAccessFlag": False,
-    "createDate": "2012-06-25T14:05:28-07:00",
-    "id": 1234,
-    "legacyCdnFlag": False,
-    "dependantServiceFlag": True,
-    "cdnSolutionName": "ORIGIN_PULL",
-    "statusId": 4,
-    "accountId": 1234,
-    "status": {'name': 'ACTIVE'},
-}
-
-getOriginPullMappingInformation = [
-    {
-        "originUrl": "http://ams01.objectstorage.softlayer.net:80",
-        "mediaType": "FLASH",
-        "id": "12345",
-        "isSecureContent": False
-    },
-    {
-        "originUrl": "http://sng01.objectstorage.softlayer.net:80",
-        "mediaType": "FLASH",
-        "id": "12345",
-        "isSecureContent": False
-    }
-]
-
-createOriginPullMapping = True
-
-deleteOriginPullRule = True
-
-loadContent = True
-
-purgeContent = [
-    {'url': 'http://z/img/0z020.png', 'statusCode': 'SUCCESS'},
-    {'url': 'http://y/img/0z010.png', 'statusCode': 'FAILED'},
-    {'url': 'http://', 'statusCode': 'INVALID_URL'}
-]
-
-purgeCache = [{'url': 'http://example.com', 'statusCode': 'SUCCESS'}]
diff --git a/SoftLayer/managers/cdn.py b/SoftLayer/managers/cdn.py
index 19a88efb..51dfb525 100644
--- a/SoftLayer/managers/cdn.py
+++ b/SoftLayer/managers/cdn.py
@@ -5,140 +5,156 @@
 
     :license: MIT, see LICENSE for more details.
 """
-import six
 
 from SoftLayer import utils
 
 
-MAX_URLS_PER_LOAD = 5
-MAX_URLS_PER_PURGE = 5
-
-
 class CDNManager(utils.IdentifierMixin, object):
-    """Manage CDN accounts and content.
+    """Manage Content Delivery Networks in the account.
 
     See product information here:
-    http://www.softlayer.com/content-delivery-network
+    https://www.ibm.com/cloud/cdn
+    https://cloud.ibm.com/docs/infrastructure/CDN?topic=CDN-about-content-delivery-networks-cdn-
 
     :param SoftLayer.API.BaseClient client: the client instance
     """
 
     def __init__(self, client):
         self.client = client
-        self.account = self.client['Network_ContentDelivery_Account']
-
-    def list_accounts(self):
-        """Lists CDN accounts for the active user."""
-
-        account = self.client['Account']
-        mask = 'cdnAccounts[%s]' % ', '.join(['id',
-                                              'createDate',
-                                              'cdnAccountName',
-                                              'cdnSolutionName',
-                                              'cdnAccountNote',
-                                              'status'])
-        return account.getObject(mask=mask).get('cdnAccounts', [])
-
-    def get_account(self, account_id, **kwargs):
-        """Retrieves a CDN account with the specified account ID.
-
-        :param account_id int: the numeric ID associated with the CDN account.
-        :param dict \\*\\*kwargs: additional arguments to include in the object
-                                mask.
-        """
+        self.cdn_configuration = self.client['Network_CdnMarketplace_Configuration_Mapping']
+        self.cdn_path = self.client['SoftLayer_Network_CdnMarketplace_Configuration_Mapping_Path']
+        self.cdn_metrics = self.client['Network_CdnMarketplace_Metrics']
+        self.cdn_purge = self.client['SoftLayer_Network_CdnMarketplace_Configuration_Cache_Purge']
 
-        if 'mask' not in kwargs:
-            kwargs['mask'] = 'status'
+    def list_cdn(self, **kwargs):
+        """Lists Content Delivery Networks for the active user.
 
-        return self.account.getObject(id=account_id, **kwargs)
+        :param dict \\*\\*kwargs: header-level options (mask, limit, etc.)
+        :returns: The list of CDN objects in the account
+        """
 
-    def get_origins(self, account_id, **kwargs):
-        """Retrieves list of origin pull mappings for a specified CDN account.
+        return self.cdn_configuration.listDomainMappings(**kwargs)
 
-        :param account_id int: the numeric ID associated with the CDN account.
-        :param dict \\*\\*kwargs: additional arguments to include in the object
-                                mask.
-        """
+    def get_cdn(self, unique_id, **kwargs):
+        """Retrieves the information about the CDN account object.
 
-        return self.account.getOriginPullMappingInformation(id=account_id,
-                                                            **kwargs)
-
-    def add_origin(self, account_id, media_type, origin_url, cname=None,
-                   secure=False):
-        """Adds an original pull mapping to an origin-pull.
-
-        :param int account_id: the numeric ID associated with the CDN account.
-        :param string media_type: the media type/protocol associated with this
-                                  origin pull mapping; valid values are HTTP,
-                                  FLASH, and WM.
-        :param string origin_url: the base URL from which content should be
-                                  pulled.
-        :param string cname: an optional CNAME that should be associated with
-                             this origin pull rule; only the hostname should be
-                             included (i.e., no 'http://', directories, etc.).
-        :param boolean secure: specifies whether this is an SSL origin pull
-                               rule, if SSL is enabled on your account
-                               (defaults to false).
+        :param str unique_id: The unique ID associated with the CDN.
+        :param dict \\*\\*kwargs: header-level option (mask)
+        :returns: The CDN object
         """
 
-        config = {'mediaType': media_type,
-                  'originUrl': origin_url,
-                  'isSecureContent': secure}
+        cdn_list = self.cdn_configuration.listDomainMappingByUniqueId(unique_id, **kwargs)
 
-        if cname:
-            config['cname'] = cname
+        # The method listDomainMappingByUniqueId() returns an array but there is only 1 object
+        return cdn_list[0]
 
-        return self.account.createOriginPullMapping(config, id=account_id)
+    def get_origins(self, unique_id, **kwargs):
+        """Retrieves list of origin pull mappings for a specified CDN account.
+
+        :param str unique_id: The unique ID associated with the CDN.
+        :param dict \\*\\*kwargs: header-level options (mask, limit, etc.)
+        :returns: The list of origin paths in the CDN object.
+        """
 
-    def remove_origin(self, account_id, origin_id):
+        return self.cdn_path.listOriginPath(unique_id, **kwargs)
+
+    def add_origin(self, unique_id, origin, path, origin_type="server", header=None,
+                   port=80, protocol='http', bucket_name=None, file_extensions=None,
+                   optimize_for="web", cache_query="include all"):
+        """Creates an origin path for an existing CDN.
+
+        :param str unique_id: The unique ID associated with the CDN.
+        :param str path: relative path to the domain provided, e.g. "/articles/video"
+        :param str origin: ip address or hostname if origin_type=server, API endpoint for
+                           your S3 object storage if origin_type=storage
+        :param str origin_type: it can be 'server' or 'storage' types.
+        :param str header: the edge server uses the host header to communicate with the origin.
+                           It defaults to hostname. (optional)
+        :param int port: the http port number (default: 80)
+        :param str protocol: the protocol of the origin (default: HTTP)
+        :param str bucket_name: name of the available resource
+        :param str file_extensions: file extensions that can be stored in the CDN, e.g. "jpg,png"
+        :param str optimize_for: performance configuration, available options: web, video, and file
+                                 where:
+                                    'web'   --> 'General web delivery'
+                                    'video' --> 'Video on demand optimization'
+                                    'file'  --> 'Large file optimization'
+        :param str cache_query: rules with the following formats: 'include-all', 'ignore-all',
+                               'include: space separated query-names',
+                               'ignore: space separated query-names'.'
+        :return: a CDN origin path object
+        """
+        types = {'server': 'HOST_SERVER', 'storage': 'OBJECT_STORAGE'}
+        performance_config = {
+            'web': 'General web delivery',
+            'video': 'Video on demand optimization',
+            'file': 'Large file optimization'
+        }
+
+        new_origin = {
+            'uniqueId': unique_id,
+            'path': path,
+            'origin': origin,
+            'originType': types.get(origin_type),
+            'httpPort': port,
+            'protocol': protocol.upper(),
+            'performanceConfiguration': performance_config.get(optimize_for, 'General web delivery'),
+            'cacheKeyQueryRule': cache_query
+        }
+
+        if header:
+            new_origin['header'] = header
+
+        if types.get(origin_type) == 'OBJECT_STORAGE':
+            if bucket_name:
+                new_origin['bucketName'] = bucket_name
+
+            if file_extensions:
+                new_origin['fileExtension'] = file_extensions
+
+        origin = self.cdn_path.createOriginPath(new_origin)
+
+        # The method createOriginPath() returns an array but there is only 1 object
+        return origin[0]
+
+    def remove_origin(self, unique_id, path):
         """Removes an origin pull mapping with the given origin pull ID.
 
-        :param int account_id: the CDN account ID from which the mapping should
-                               be deleted.
-        :param int origin_id: the origin pull mapping ID to delete.
+        :param str unique_id: The unique ID associated with the CDN.
+        :param str path: The origin path to delete.
+        :returns: A string value
         """
 
-        return self.account.deleteOriginPullRule(origin_id, id=account_id)
+        return self.cdn_path.deleteOriginPath(unique_id, path)
 
-    def load_content(self, account_id, urls):
-        """Prefetches one or more URLs to the CDN edge nodes.
+    def purge_content(self, unique_id, path):
+        """Purges a URL or path from the CDN.
 
-        :param int account_id: the CDN account ID into which content should be
-                               preloaded.
-        :param urls: a string or a list of strings representing the CDN URLs
-                     that should be pre-loaded.
-        :returns: true if all load requests were successfully submitted;
-                  otherwise, returns the first error encountered.
+        :param str unique_id: The unique ID associated with the CDN.
+        :param str path: A string of url or path that should be purged.
+        :returns: A Container_Network_CdnMarketplace_Configuration_Cache_Purge array object
         """
 
-        if isinstance(urls, six.string_types):
-            urls = [urls]
-
-        for i in range(0, len(urls), MAX_URLS_PER_LOAD):
-            result = self.account.loadContent(urls[i:i + MAX_URLS_PER_LOAD],
-                                              id=account_id)
-            if not result:
-                return result
+        return self.cdn_purge.createPurge(unique_id, path)
 
-        return True
+    def get_usage_metrics(self, unique_id, history=30, frequency="aggregate"):
+        """Retrieves the cdn usage metrics.
 
-    def purge_content(self, account_id, urls):
-        """Purges one or more URLs from the CDN edge nodes.
+        It uses the 'days' argument if start_date and end_date are None.
 
-        :param int account_id: the CDN account ID from which content should
-                               be purged.
-        :param urls: a string or a list of strings representing the CDN URLs
-                     that should be purged.
-        :returns: a list of SoftLayer_Container_Network_ContentDelivery_PurgeService_Response objects
-                  which indicates if the purge for each url was SUCCESS, FAILED or INVALID_URL.
+        :param int unique_id: The CDN uniqueId from which the usage metrics will be obtained.
+        :param int history: Last N days, default days is 30.
+        :param str frequency: It can be day, week, month and aggregate. The default is "aggregate".
+        :returns: A Container_Network_CdnMarketplace_Metrics object
         """
 
-        if isinstance(urls, six.string_types):
-            urls = [urls]
+        _start = utils.days_to_datetime(history)
+        _end = utils.days_to_datetime(0)
+
+        _start_date = utils.timestamp(_start)
+        _end_date = utils.timestamp(_end)
 
-        content_list = []
-        for i in range(0, len(urls), MAX_URLS_PER_PURGE):
-            content = self.account.purgeCache(urls[i:i + MAX_URLS_PER_PURGE], id=account_id)
-            content_list.extend(content)
+        usage = self.cdn_metrics.getMappingUsageMetrics(unique_id, _start_date, _end_date, frequency)
 
-        return content_list
+        # The method getMappingUsageMetrics() returns an array but there is only 1 object
+        return usage[0]
diff --git a/SoftLayer/utils.py b/SoftLayer/utils.py
index f4904adf..b7099784 100644
--- a/SoftLayer/utils.py
+++ b/SoftLayer/utils.py
@@ -7,6 +7,7 @@
 """
 import datetime
 import re
+import time
 
 import six
 
@@ -311,3 +312,30 @@ def clean_time(sltime, in_format='%Y-%m-%dT%H:%M:%S%z', out_format='%Y-%m-%d %H:
     # The %z option only exists with py3.6+
     except ValueError:
         return sltime
+
+
+def timestamp(date):
+    """Converts a datetime to timestamp
+
+    :param datetime date:
+    :returns int: The timestamp of date.
+    """
+
+    _timestamp = time.mktime(date.timetuple())
+
+    return int(_timestamp)
+
+
+def days_to_datetime(days):
+    """Returns the datetime value of last N days.
+
+    :param int days: From 0 to N days
+    :returns int: The datetime of last N days or datetime.now() if days <= 0.
+    """
+
+    date = datetime.datetime.now()
+
+    if days > 0:
+        date -= datetime.timedelta(days=days)
+
+    return date
diff --git a/docs/cli/cdn.rst b/docs/cli/cdn.rst
new file mode 100644
index 00000000..fce54f73
--- /dev/null
+++ b/docs/cli/cdn.rst
@@ -0,0 +1,29 @@
+.. _cli_cdn:
+
+Interacting with CDN
+==============================
+
+
+.. click:: SoftLayer.CLI.cdn.detail:cli
+   :prog: cdn detail
+   :show-nested:
+
+.. click:: SoftLayer.CLI.cdn.list:cli
+   :prog: cdn list
+   :show-nested:
+
+.. click:: SoftLayer.CLI.cdn.origin_add:cli
+   :prog: cdn origin-add
+   :show-nested:
+
+.. click:: SoftLayer.CLI.cdn.origin_list:cli
+   :prog: cdn origin-list
+   :show-nested:
+
+.. click:: SoftLayer.CLI.cdn.origin_remove:cli
+   :prog: cdn origin-remove
+   :show-nested:
+
+.. click:: SoftLayer.CLI.cdn.purge:cli
+   :prog: cdn purge
+   :show-nested:


Test patch:
diff --git a/tests/CLI/modules/cdn_tests.py b/tests/CLI/modules/cdn_tests.py
index b39e8b8e..cb3c59e4 100644
--- a/tests/CLI/modules/cdn_tests.py
+++ b/tests/CLI/modules/cdn_tests.py
@@ -4,10 +4,11 @@
 
     :license: MIT, see LICENSE for more details.
 """
-from SoftLayer import testing
-
 import json
 
+from SoftLayer.CLI import exceptions
+from SoftLayer import testing
+
 
 class CdnTests(testing.TestCase):
 
@@ -16,67 +17,81 @@ def test_list_accounts(self):
 
         self.assert_no_fail(result)
         self.assertEqual(json.loads(result.output),
-                         [{'notes': None,
-                           'created': '2012-06-25T14:05:28-07:00',
-                           'type': 'ORIGIN_PULL',
-                           'id': 1234,
-                           'account_name': '1234a'},
-                          {'notes': None,
-                           'created': '2012-07-24T13:34:25-07:00',
-                           'type': 'POP_PULL',
-                           'id': 1234,
-                           'account_name': '1234a'}])
+                         [{'cname': 'cdnakauuiet7s6u6.cdnedge.bluemix.net',
+                           'domain': 'test.example.com',
+                           'origin': '1.1.1.1',
+                           'status': 'CNAME_CONFIGURATION',
+                           'unique_id': '9934111111111',
+                           'vendor': 'akamai'}]
+                         )
 
     def test_detail_account(self):
-        result = self.run_command(['cdn', 'detail', '1245'])
+        result = self.run_command(['cdn', 'detail', '--history=30', '1245'])
 
         self.assert_no_fail(result)
         self.assertEqual(json.loads(result.output),
-                         {'notes': None,
-                          'created': '2012-06-25T14:05:28-07:00',
-                          'type': 'ORIGIN_PULL',
-                          'status': 'ACTIVE',
-                          'id': 1234,
-                          'account_name': '1234a'})
-
-    def test_load_content(self):
-        result = self.run_command(['cdn', 'load', '1234',
-                                   'http://example.com'])
-
-        self.assert_no_fail(result)
-        self.assertEqual(result.output, "")
+                         {'hit_radio': '0.0 %',
+                          'hostname': 'test.example.com',
+                          'origin': '1.1.1.1',
+                          'origin_type': 'HOST_SERVER',
+                          'path': '/',
+                          'protocol': 'HTTP',
+                          'provider': 'akamai',
+                          'status': 'CNAME_CONFIGURATION',
+                          'total_bandwidth': '0.0 GB',
+                          'total_hits': '0',
+                          'unique_id': '9934111111111'}
+                         )
 
     def test_purge_content(self):
         result = self.run_command(['cdn', 'purge', '1234',
-                                   'http://example.com'])
-        expected = [{"url": "http://example.com", "status": "SUCCESS"}]
+                                   '/article/file.txt'])
+
         self.assert_no_fail(result)
-        self.assertEqual(json.loads(result.output), expected)
 
     def test_list_origins(self):
         result = self.run_command(['cdn', 'origin-list', '1234'])
 
         self.assert_no_fail(result)
-        self.assertEqual(json.loads(result.output), [
-            {'media_type': 'FLASH',
-             'origin_url': 'http://ams01.objectstorage.softlayer.net:80',
-             'cname': None,
-             'id': '12345'},
-            {'media_type': 'FLASH',
-             'origin_url': 'http://sng01.objectstorage.softlayer.net:80',
-             'cname': None,
-             'id': '12345'}])
-
-    def test_add_origin(self):
-        result = self.run_command(['cdn', 'origin-add', '1234',
-                                   'http://example.com'])
+        self.assertEqual(json.loads(result.output), [{'HTTP Port': 80,
+                                                      'Origin': '10.10.10.1',
+                                                      'Path': '/example',
+                                                      'Status': 'RUNNING'},
+                                                     {'HTTP Port': 80,
+                                                      'Origin': '10.10.10.1',
+                                                      'Path': '/example1',
+                                                      'Status': 'RUNNING'}])
+
+    def test_add_origin_server(self):
+        result = self.run_command(
+            ['cdn', 'origin-add', '-t', 'server', '-H=test.example.com', '-p', 80, '-o', 'web', '-c=include-all',
+             '1234', '10.10.10.1', '/example/videos2'])
+
+        self.assert_no_fail(result)
+
+    def test_add_origin_storage(self):
+        result = self.run_command(['cdn', 'origin-add', '-t', 'storage', '-b=test-bucket', '-H=test.example.com',
+                                   '-p', 80, '-o', 'web', '-c=include-all', '1234', '10.10.10.1', '/example/videos2'])
+
+        self.assert_no_fail(result)
+
+    def test_add_origin_without_storage(self):
+        result = self.run_command(['cdn', 'origin-add', '-t', 'storage', '-H=test.example.com', '-p', 80,
+                                   '-o', 'web', '-c=include-all', '1234', '10.10.10.1', '/example/videos2'])
+
+        self.assertEqual(result.exit_code, 2)
+        self.assertIsInstance(result.exception, exceptions.ArgumentError)
+
+    def test_add_origin_storage_with_file_extensions(self):
+        result = self.run_command(
+            ['cdn', 'origin-add', '-t', 'storage', '-b=test-bucket', '-e', 'jpg', '-H=test.example.com', '-p', 80,
+             '-o', 'web', '-c=include-all', '1234', '10.10.10.1', '/example/videos2'])
 
         self.assert_no_fail(result)
-        self.assertEqual(result.output, "")
 
     def test_remove_origin(self):
         result = self.run_command(['cdn', 'origin-remove', '1234',
-                                   'http://example.com'])
+                                   '/example1'])
 
         self.assert_no_fail(result)
-        self.assertEqual(result.output, "")
+        self.assertEqual(result.output, "Origin with path /example1 has been deleted\n")
diff --git a/tests/managers/cdn_tests.py b/tests/managers/cdn_tests.py
index 8de4bd50..41a675c6 100644
--- a/tests/managers/cdn_tests.py
+++ b/tests/managers/cdn_tests.py
@@ -4,12 +4,10 @@
 
     :license: MIT, see LICENSE for more details.
 """
-import math
-import mock
 
-from SoftLayer import fixtures
 from SoftLayer.managers import cdn
 from SoftLayer import testing
+from SoftLayer import utils
 
 
 class CDNTests(testing.TestCase):
@@ -18,123 +16,96 @@ def set_up(self):
         self.cdn_client = cdn.CDNManager(self.client)
 
     def test_list_accounts(self):
-        accounts = self.cdn_client.list_accounts()
-        self.assertEqual(accounts, fixtures.SoftLayer_Account.getCdnAccounts)
+        self.cdn_client.list_cdn()
+        self.assert_called_with('SoftLayer_Network_CdnMarketplace_Configuration_Mapping',
+                                'listDomainMappings')
 
-    def test_get_account(self):
-        account = self.cdn_client.get_account(12345)
-        self.assertEqual(
-            account,
-            fixtures.SoftLayer_Network_ContentDelivery_Account.getObject)
+    def test_detail_cdn(self):
+        self.cdn_client.get_cdn("12345")
 
-    def test_get_origins(self):
-        origins = self.cdn_client.get_origins(12345)
-        self.assertEqual(
-            origins,
-            fixtures.SoftLayer_Network_ContentDelivery_Account.
-            getOriginPullMappingInformation)
-
-    def test_add_origin(self):
-        self.cdn_client.add_origin(12345,
-                                   'http',
-                                   'http://localhost/',
-                                   'self.local',
-                                   False)
+        args = ("12345",)
+        self.assert_called_with('SoftLayer_Network_CdnMarketplace_Configuration_Mapping',
+                                'listDomainMappingByUniqueId',
+                                args=args)
 
-        args = ({
-            'mediaType': 'http',
-            'originUrl': 'http://localhost/',
-            'cname': 'self.local',
-            'isSecureContent': False
-        },)
-        self.assert_called_with('SoftLayer_Network_ContentDelivery_Account',
-                                'createOriginPullMapping',
-                                args=args,
-                                identifier=12345)
+    def test_detail_usage_metric(self):
+        self.cdn_client.get_usage_metrics(12345, history=30, frequency="aggregate")
 
-    def test_remove_origin(self):
-        self.cdn_client.remove_origin(12345, 12345)
-        self.assert_called_with('SoftLayer_Network_ContentDelivery_Account',
-                                'deleteOriginPullRule',
-                                args=(12345,),
-                                identifier=12345)
-
-    def test_load_content(self):
-        urls = ['http://a/img/0x001.png',
-                'http://b/img/0x002.png',
-                'http://c/img/0x004.png',
-                'http://d/img/0x008.png',
-                'http://e/img/0x010.png',
-                'http://e/img/0x020.png']
-
-        self.cdn_client.load_content(12345, urls)
-        calls = self.calls('SoftLayer_Network_ContentDelivery_Account',
-                           'loadContent')
-        self.assertEqual(len(calls),
-                         math.ceil(len(urls) / float(cdn.MAX_URLS_PER_LOAD)))
-
-    def test_load_content_single(self):
-        url = 'http://geocities.com/Area51/Meteor/12345/under_construction.gif'
-        self.cdn_client.load_content(12345, url)
-
-        self.assert_called_with('SoftLayer_Network_ContentDelivery_Account',
-                                'loadContent',
-                                args=([url],),
-                                identifier=12345)
-
-    def test_load_content_failure(self):
-        urls = ['http://z/img/0x004.png',
-                'http://y/img/0x002.png',
-                'http://x/img/0x001.png']
-
-        service = self.client['SoftLayer_Network_ContentDelivery_Account']
-        service.loadContent.return_value = False
-
-        self.cdn_client.load_content(12345, urls)
-        calls = self.calls('SoftLayer_Network_ContentDelivery_Account',
-                           'loadContent')
-        self.assertEqual(len(calls),
-                         math.ceil(len(urls) / float(cdn.MAX_URLS_PER_LOAD)))
-
-    def test_purge_content(self):
-        urls = ['http://z/img/0x020.png',
-                'http://y/img/0x010.png',
-                'http://x/img/0x008.png',
-                'http://w/img/0x004.png',
-                'http://v/img/0x002.png',
-                'http://u/img/0x001.png']
+        _start = utils.days_to_datetime(30)
+        _end = utils.days_to_datetime(0)
 
-        self.cdn_client.purge_content(12345, urls)
-        calls = self.calls('SoftLayer_Network_ContentDelivery_Account',
-                           'purgeCache')
-        self.assertEqual(len(calls),
-                         math.ceil(len(urls) / float(cdn.MAX_URLS_PER_PURGE)))
+        _start_date = utils.timestamp(_start)
+        _end_date = utils.timestamp(_end)
 
-    def test_purge_content_failure(self):
-        urls = ['http://',
-                'http://y/img/0x002.png',
-                'http://x/img/0x001.png']
+        args = (12345,
+                _start_date,
+                _end_date,
+                "aggregate")
+        self.assert_called_with('SoftLayer_Network_CdnMarketplace_Metrics',
+                                'getMappingUsageMetrics',
+                                args=args)
 
-        contents = [
-            {'url': urls[0], 'statusCode': 'INVALID_URL'},
-            {'url': urls[1], 'statusCode': 'FAILED'},
-            {'url': urls[2], 'statusCode': 'FAILED'}
-        ]
+    def test_get_origins(self):
+        self.cdn_client.get_origins("12345")
+        self.assert_called_with('SoftLayer_Network_CdnMarketplace_Configuration_Mapping_Path',
+                                'listOriginPath')
 
-        self.cdn_client.account = mock.Mock()
-        self.cdn_client.account.purgeCache.return_value = contents
+    def test_add_origin(self):
+        self.cdn_client.add_origin("12345", "10.10.10.1", "/example/videos", origin_type="server",
+                                   header="test.example.com", port=80, protocol='http', optimize_for="web",
+                                   cache_query="include all")
 
-        result = self.cdn_client.purge_content(12345, urls)
+        args = ({
+                    'uniqueId': "12345",
+                    'origin': '10.10.10.1',
+                    'path': '/example/videos',
+                    'originType': 'HOST_SERVER',
+                    'header': 'test.example.com',
+                    'httpPort': 80,
+                    'protocol': 'HTTP',
+                    'performanceConfiguration': 'General web delivery',
+                    'cacheKeyQueryRule': "include all"
+                },)
+        self.assert_called_with('SoftLayer_Network_CdnMarketplace_Configuration_Mapping_Path',
+                                'createOriginPath',
+                                args=args)
+
+    def test_add_origin_with_bucket_and_file_extension(self):
+        self.cdn_client.add_origin("12345", "10.10.10.1", "/example/videos", origin_type="storage",
+                                   bucket_name="test-bucket", file_extensions="jpg", header="test.example.com", port=80,
+                                   protocol='http', optimize_for="web", cache_query="include all")
 
-        self.assertEqual(contents, result)
+        args = ({
+                    'uniqueId': "12345",
+                    'origin': '10.10.10.1',
+                    'path': '/example/videos',
+                    'originType': 'OBJECT_STORAGE',
+                    'header': 'test.example.com',
+                    'httpPort': 80,
+                    'protocol': 'HTTP',
+                    'bucketName': 'test-bucket',
+                    'fileExtension': 'jpg',
+                    'performanceConfiguration': 'General web delivery',
+                    'cacheKeyQueryRule': "include all"
+                },)
+        self.assert_called_with('SoftLayer_Network_CdnMarketplace_Configuration_Mapping_Path',
+                                'createOriginPath',
+                                args=args)
 
-    def test_purge_content_single(self):
-        url = 'http://geocities.com/Area51/Meteor/12345/under_construction.gif'
-        self.cdn_client.account = mock.Mock()
-        self.cdn_client.account.purgeCache.return_value = [{'url': url, 'statusCode': 'SUCCESS'}]
+    def test_remove_origin(self):
+        self.cdn_client.remove_origin("12345", "/example1")
 
-        expected = [{'url': url, 'statusCode': 'SUCCESS'}]
+        args = ("12345",
+                "/example1")
+        self.assert_called_with('SoftLayer_Network_CdnMarketplace_Configuration_Mapping_Path',
+                                'deleteOriginPath',
+                                args=args)
 
-        result = self.cdn_client.purge_content(12345, url)
+    def test_purge_content(self):
+        self.cdn_client.purge_content("12345", "/example1")
 
-        self.assertEqual(expected, result)
+        args = ("12345",
+                "/example1")
+        self.assert_called_with('SoftLayer_Network_CdnMarketplace_Configuration_Cache_Purge',
+                                'createPurge',
+                                args=args)