Repository: softlayer/softlayer-python
Pull request #1898 (2023-04-04 14:57:32)

Added new command - slcli cdn create
Issue: #1897 
```
softlayer-python/ (issue1897 *) $ slcli cdn create --hostname www.test.com --origin 123.123.123.123 --http 80
┌──────────────────┬──────────────────────────────────────┐
│       Name       │                Value                 │
├──────────────────┼──────────────────────────────────────┤
│  CDN Unique ID   │           27048910            │
│     Hostname     │           www.test.com              │
│      Header      │           www.test.com              │
│    IBM CNAME     │ cdnakabnnssbcsiz.cdn.appdomain.cloud │
│   Akamai CNAME   │  wildcard.appdomain.mdc.edgekey.net  │
│   Origin Host    │              123.21.2.2              │
│   Origin Type    │             HOST_SERVER              │
│     Protocol     │                 HTTP                 │
│    Http Port     │                  80                  │
│    Https Port    │                                      │
│ Certificate Type │               NO_CERT                │
│     Provider     │                akamai                │
│       Path       │                  /*                  │
└──────────────────┴──────────────────────────────────────┘
```


Patch:
diff --git a/SoftLayer/CLI/cdn/create.py b/SoftLayer/CLI/cdn/create.py
new file mode 100644
index 00000000..c23d91e5
--- /dev/null
+++ b/SoftLayer/CLI/cdn/create.py
@@ -0,0 +1,56 @@
+"""Create a CDN domain mapping."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import exceptions
+from SoftLayer.CLI import formatting
+
+
+@click.command(cls=SoftLayer.CLI.command.SLCommand, )
+@click.option('--hostname', required=True, help="To route requests to your website, enter the hostname for your"
+              "website, for example, www.example.com or app.example.com.")
+@click.option('--origin', required=True, help="Your server IP address or hostname.")
+@click.option('--origin-type', default="server", type=click.Choice(['server', 'storage']), show_default=True,
+              help="The origin type. Note: If OriginType is storage then OriginHost is take as Endpoint")
+@click.option('--http', help="Http port")
+@click.option('--https', help="Https port")
+@click.option('--bucket-name', help="Bucket name")
+@click.option('--cname', help="Enter a globally unique subdomain. The full URL becomes the CNAME we use to configure"
+              " your DNS. If no value is entered, we will generate a CNAME for you.")
+@click.option('--header', help="The edge server uses the host header in the HTTP header to communicate with the"
+              " Origin host. It defaults to Hostname.")
+@click.option('--path', help="Give a path relative to the domain provided, which can be used to reach this Origin."
+              " For example, 'articles/video' => 'www.example.com/articles/video")
+@click.option('--ssl', default="dvSan", type=click.Choice(['dvSan', 'wilcard']), help="A DV SAN Certificate allows"
+              " HTTPS traffic over your personal domain, but it requires a domain validation to prove ownership."
+              " A wildcard certificate allows HTTPS traffic only when using the CNAME given.")
+@environment.pass_env
+def cli(env, hostname, origin, origin_type, http, https, bucket_name, cname, header, path, ssl):
+    """Create a CDN domain mapping."""
+    if not http and not https:
+        raise exceptions.CLIAbort('Is needed http or https options')
+
+    manager = SoftLayer.CDNManager(env.client)
+    cdn = manager.create_cdn(hostname, origin, origin_type, http, https, bucket_name, cname, header, path, ssl)
+
+    table = formatting.Table(['Name', 'Value'])
+    table.add_row(['CDN Unique ID', cdn.get('uniqueId')])
+    if bucket_name:
+        table.add_row(['Bucket Name', cdn.get('bucketName')])
+    table.add_row(['Hostname', cdn.get('domain')])
+    table.add_row(['Header', cdn.get('header')])
+    table.add_row(['IBM CNAME', cdn.get('cname')])
+    table.add_row(['Akamai CNAME', cdn.get('akamaiCname')])
+    table.add_row(['Origin Host', cdn.get('originHost')])
+    table.add_row(['Origin Type', cdn.get('originType')])
+    table.add_row(['Protocol', cdn.get('protocol')])
+    table.add_row(['Http Port', cdn.get('httpPort')])
+    table.add_row(['Https Port', cdn.get('httpsPort')])
+    table.add_row(['Certificate Type', cdn.get('certificateType')])
+    table.add_row(['Provider', cdn.get('vendorName')])
+    table.add_row(['Path', cdn.get('path')])
+
+    env.fout(table)
diff --git a/SoftLayer/CLI/cdn/delete.py b/SoftLayer/CLI/cdn/delete.py
new file mode 100644
index 00000000..83f1ee63
--- /dev/null
+++ b/SoftLayer/CLI/cdn/delete.py
@@ -0,0 +1,22 @@
+"""Delete a CDN domain mapping."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI import environment
+
+
+@click.command(cls=SoftLayer.CLI.command.SLCommand, )
+@click.argument('unique_id')
+@environment.pass_env
+def cli(env, unique_id):
+    """Delete a CDN domain mapping."""
+
+    manager = SoftLayer.CDNManager(env.client)
+
+    # cdn_mapping = manager.get_cdn(unique_id)
+    cdn = manager.delete_cdn(unique_id)
+
+    if cdn:
+        env.fout("Cdn with uniqueId: {} was deleted.".format(unique_id))
diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py
index 921ad247..63f8e7b7 100644
--- a/SoftLayer/CLI/routes.py
+++ b/SoftLayer/CLI/routes.py
@@ -81,6 +81,8 @@
     ('cdn:origin-list', 'SoftLayer.CLI.cdn.origin_list:cli'),
     ('cdn:origin-remove', 'SoftLayer.CLI.cdn.origin_remove:cli'),
     ('cdn:purge', 'SoftLayer.CLI.cdn.purge:cli'),
+    ('cdn:delete', 'SoftLayer.CLI.cdn.delete:cli'),
+    ('cdn:create', 'SoftLayer.CLI.cdn.create:cli'),
 
     ('config', 'SoftLayer.CLI.config'),
     ('config:setup', 'SoftLayer.CLI.config.setup:cli'),
diff --git a/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Configuration_Mapping.py b/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Configuration_Mapping.py
index e080c40e..46b3e675 100644
--- a/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Configuration_Mapping.py
+++ b/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Configuration_Mapping.py
@@ -50,3 +50,52 @@
         "cacheKeyQueryRule": "include: test"
     }
 ]
+
+deleteDomainMapping = [
+    {
+        "akamaiCname": "wildcard.appdomain.mdc.edgekey.net",
+        "certificateType": "NO_CERT",
+        "cname": "cdnakayq9fye4t88239.cdn.appdomain.cloud",
+        "createDate": "2023-03-29T07:33:42-06:00",
+        "domain": "www.test.com",
+        "header": "www.header.com",
+        "httpPort": 80,
+        "httpsPort": None,
+        "modifyDate": "2023-03-29T07:33:46-06:00",
+        "originHost": "67.228.227.82",
+        "originType": "HOST_SERVER",
+        "path": "/path/*",
+        "performanceConfiguration": "General web delivery",
+        "protocol": "HTTP",
+        "respectHeaders": False,
+        "serveStale": True,
+        "status": "DELETING",
+        "uniqueId": "303727924488685",
+        "vendorName": "akamai"
+    }
+]
+createDomainMapping = [
+    {
+        "bucketName": "test-bucket-name",
+        "akamaiCname": "wildcard.appdomain.mdc.edgekey.net",
+        "cacheKeyQueryRule": "include-all",
+        "certificateType": "WILDCARD_CERT",
+        "cname": "test.cdn.appdomain.cloud",
+        "createDate": "2020-09-29T15:19:01-06:00",
+        "domain": "test.com",
+        "header": "header.test.com",
+        "httpPort": 80,
+        "httpsPort": None,
+        "modifyDate": "2021-06-24T09:02:22-06:00",
+        "originHost": "10.32.12.125",
+        "originType": "HOST_SERVER",
+        "path": "/*",
+        "performanceConfiguration": "General web delivery",
+        "protocol": "HTTP",
+        "respectHeaders": True,
+        "serveStale": True,
+        "status": "CNAME_CONFIGURATION",
+        "uniqueId": "354034879028850",
+        "vendorName": "akamai"
+    }
+]
diff --git a/SoftLayer/managers/cdn.py b/SoftLayer/managers/cdn.py
index 1c6597b4..0b9aa0a7 100644
--- a/SoftLayer/managers/cdn.py
+++ b/SoftLayer/managers/cdn.py
@@ -269,3 +269,71 @@ def get_cache_key_query_rule(cache_type, cache):
         cache_result = '%s: %s' % (cache_type, cache['description'])
 
         return cache_result
+
+    def delete_cdn(self, unique_id):
+        """Delete CDN domain mapping for a particular customer.
+
+        :param str unique_id: The unique ID associated with the CDN.
+        :returns: The cdn that is being deleted.
+        """
+
+        return self.cdn_configuration.deleteDomainMapping(unique_id)
+
+    def create_cdn(self, hostname=None, origin=None, origin_type=None, http=None, https=None, bucket_name=None,
+                   cname=None, header=None, path=None, ssl=None):
+        """Create CDN domain mapping for a particular customer.
+
+        :param str hostname: The unique ID associated with the CDN.
+        :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 int http: http port
+        :param int https: https port
+        :param str bucket_name: name of the available resource
+        :param str cname: globally unique subdomain
+        :param str header: the edge server uses the host header to communicate with the origin.
+                            It defaults to hostname. (optional)
+        :param str path: relative path to the domain provided, e.g. "/articles/video"
+        :param str ssl: ssl certificate
+        :returns: The cdn that is being created.
+        """
+        types = {'server': 'HOST_SERVER', 'storage': 'OBJECT_STORAGE'}
+        ssl_certificate = {'wilcard': 'WILDCARD_CERT', 'dvSan': 'SHARED_SAN_CERT'}
+
+        new_origin = {
+            'domain': hostname,
+            'origin': origin,
+            'originType': types.get(origin_type),
+            'vendorName': 'akamai',
+        }
+
+        protocol = ''
+        if http:
+            protocol = 'HTTP'
+            new_origin['httpPort'] = http
+        if https:
+            protocol = 'HTTPS'
+            new_origin['httpsPort'] = https
+            new_origin['certificateType'] = ssl_certificate.get(ssl)
+        if http and https:
+            protocol = 'HTTP_AND_HTTPS'
+
+        new_origin['protocol'] = protocol
+
+        if types.get(origin_type) == 'OBJECT_STORAGE':
+            new_origin['bucketName'] = bucket_name
+            new_origin['header'] = header
+
+        if cname:
+            new_origin['cname'] = cname + '.cdn.appdomain.cloud'
+
+        if header:
+            new_origin['header'] = header
+
+        if path:
+            new_origin['path'] = '/' + path
+
+        origin = self.cdn_configuration.createDomainMapping(new_origin)
+
+        # The method createOriginPath() returns an array but there is only 1 object
+        return origin[0]
diff --git a/docs/cli/cdn.rst b/docs/cli/cdn.rst
index 3b749995..de9e7c6f 100644
--- a/docs/cli/cdn.rst
+++ b/docs/cli/cdn.rst
@@ -31,3 +31,11 @@ Interacting with CDN
 .. click:: SoftLayer.CLI.cdn.edit:cli
    :prog: cdn edit
    :show-nested:
+
+.. click:: SoftLayer.CLI.cdn.delete:cli
+   :prog: cdn delete
+   :show-nested:
+
+.. click:: SoftLayer.CLI.cdn.create:cli
+   :prog: cdn create
+   :show-nested:
\ No newline at end of file


Test patch:
diff --git a/tests/CLI/modules/cdn_tests.py b/tests/CLI/modules/cdn_tests.py
index a7dfa340..2e368261 100644
--- a/tests/CLI/modules/cdn_tests.py
+++ b/tests/CLI/modules/cdn_tests.py
@@ -123,3 +123,40 @@ def test_edit_cache_by_uniqueId(self):
         self.assert_no_fail(result)
         header_result = json.loads(result.output)
         self.assertEqual('include: test', header_result['Cache key optimization'])
+
+    def test_delete_cdn(self):
+        result = self.run_command(['cdn', 'delete', '123456'])
+        self.assert_no_fail(result)
+        self.assertIn("Cdn with uniqueId: 123456 was deleted.", result.output)
+
+    def test_create_cdn(self):
+        result = self.run_command(['cdn', 'create', '--hostname', 'www.example.com',
+                                  '--origin', '123.123.123.123', '--http', '80'])
+        self.assert_no_fail(result)
+        self.assertIn("CDN Unique ID", result.output)
+        self.assertIn("354034879028850", result.output)
+        self.assertIn("Hostname", result.output)
+        self.assertIn("test.com", result.output)
+        self.assertIn("header", result.output)
+        self.assertIn("header.test.com", result.output)
+        self.assertIn("Http Port", result.output)
+        self.assertIn("80", result.output)
+        self.assertIn("Path", result.output)
+        self.assertIn("/*", result.output)
+
+    def test_create_cdn_without_hostname(self):
+        result = self.run_command(['cdn', 'create'])
+        self.assertEqual(2, result.exit_code)
+        print(result.output)
+        self.assertIn("Error: Missing option '--hostname'.", result.output)
+
+    def test_create_cdn_without_origin(self):
+        result = self.run_command(['cdn', 'create', '--hostname', 'www.example.com'])
+        self.assertEqual(2, result.exit_code)
+        print(result.output)
+        self.assertIn("Error: Missing option '--origin'.", result.output)
+
+    def test_create_cdn_without_http_or_https(self):
+        result = self.run_command(['cdn', 'create', '--hostname', 'www.example.com', '--origin', '123.123.123.123'])
+        self.assertEqual(2, result.exit_code)
+        self.assertIn("Is needed http or https options", result.exception.message)
diff --git a/tests/managers/cdn_tests.py b/tests/managers/cdn_tests.py
index b0e641c6..6a220e28 100644
--- a/tests/managers/cdn_tests.py
+++ b/tests/managers/cdn_tests.py
@@ -150,3 +150,26 @@ def test_cdn_instance_by_hostname(self):
         self.assert_called_with(
             'SoftLayer_Network_CdnMarketplace_Configuration_Mapping',
             'listDomainMappings',)
+
+    def test_delete_cdn(self):
+        uniqueId = "123465"
+        self.cdn_client.delete_cdn(uniqueId)
+
+        args = (uniqueId,)
+
+        self.assert_called_with('SoftLayer_Network_CdnMarketplace_Configuration_Mapping',
+                                'deleteDomainMapping',
+                                args=args)
+
+    def test_create_cdn(self):
+        hostname = "test.com"
+        origin = "123.123.123.123"
+        origin_type = "server"
+        http = 80
+        newCdn = ({"domain": hostname, "origin": origin, "originType": "HOST_SERVER",
+                  "vendorName": "akamai", "httpPort": http, "protocol": "HTTP"},)
+        self.cdn_client.create_cdn(hostname, origin, origin_type, http)
+
+        self.assert_called_with('SoftLayer_Network_CdnMarketplace_Configuration_Mapping',
+                                'createDomainMapping',
+                                args=newCdn)