Add Records and Servers CLI commands

Change-Id: I61249dc1d9d70b35255a1cad7080cba306a85d42
This commit is contained in:
Kiall Mac Innes 2012-11-28 13:28:14 +00:00
parent 68319c6e89
commit 18cae5e5ee
11 changed files with 489 additions and 51 deletions

@ -76,11 +76,6 @@ class ListCommand(Command, Lister):
class GetCommand(Command, ShowOne):
def get_parser(self, prog_name):
parser = super(GetCommand, self).get_parser(prog_name)
parser.add_argument('id', help='The ID or Name to get')
return parser
def post_execute(self, results):
return results.keys(), results.values()

@ -33,12 +33,12 @@ class GetDomainCommand(base.GetCommand):
def get_parser(self, prog_name):
parser = super(GetDomainCommand, self).get_parser(prog_name)
parser.add_argument('--domain-id', help="Domain ID", required=True)
parser.add_argument('id', help="Domain ID", required=True)
return parser
def execute(self, parsed_args):
return self.client.domains.get(parsed_args.domain_id)
return self.client.domains.get(parsed_args.id)
class CreateDomainCommand(base.CreateCommand):
@ -47,16 +47,15 @@ class CreateDomainCommand(base.CreateCommand):
def get_parser(self, prog_name):
parser = super(CreateDomainCommand, self).get_parser(prog_name)
parser.add_argument('--domain-name', help="Domain Name", required=True)
parser.add_argument('--domain-email', help="Domain Email",
required=True)
parser.add_argument('--name', help="Domain Name", required=True)
parser.add_argument('--email', help="Domain Email", required=True)
return parser
def execute(self, parsed_args):
domain = Domain(
name=parsed_args.domain_name,
email=parsed_args.domain_email
name=parsed_args.name,
email=parsed_args.email,
)
return self.client.domains.create(domain)
@ -68,18 +67,22 @@ class UpdateDomainCommand(base.UpdateCommand):
def get_parser(self, prog_name):
parser = super(UpdateDomainCommand, self).get_parser(prog_name)
parser.add_argument('--domain-id', help="Domain ID", required=True)
parser.add_argument('--domain-name', help="Domain Name")
parser.add_argument('--domain-email', help="Domain Email")
parser.add_argument('id', help="Domain ID", required=True)
parser.add_argument('--name', help="Domain Name")
parser.add_argument('--email', help="Domain Email")
return parser
def execute(self, parsed_args):
# TODO: API needs updating.. this get is silly
domain = self.client.domains.get(parsed_args.domain_id)
domain = self.client.domains.get(parsed_args.id)
# TODO: How do we tell if an arg was supplied or intentionally set to
# None?
domain.update({
'name': parsed_args.name,
'email': parsed_args.email,
})
return self.client.domains.update(domain)
@ -90,9 +93,9 @@ class DeleteDomainCommand(base.DeleteCommand):
def get_parser(self, prog_name):
parser = super(DeleteDomainCommand, self).get_parser(prog_name)
parser.add_argument('--domain-id', help="Domain ID")
parser.add_argument('id', help="Domain ID", required=True)
return parser
def execute(self, parsed_args):
return self.client.domains.delete(parsed_args.domain_id)
return self.client.domains.delete(parsed_args.id)

@ -0,0 +1,121 @@
# Copyright 2012 Managed I.T.
#
# Author: Kiall Mac Innes <kiall@managedit.ie>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from monikerclient.cli import base
from monikerclient.v1.records import Record
LOG = logging.getLogger(__name__)
class ListRecordsCommand(base.ListCommand):
""" List Records """
def get_parser(self, prog_name):
parser = super(ListRecordsCommand, self).get_parser(prog_name)
parser.add_argument('domain_id', help="Domain ID")
return parser
def execute(self, parsed_args):
return self.client.records.list(parsed_args.domain_id)
class GetRecordCommand(base.GetCommand):
""" Get Record """
def get_parser(self, prog_name):
parser = super(GetRecordCommand, self).get_parser(prog_name)
parser.add_argument('domain_id', help="Domain ID")
parser.add_argument('id', help="Record ID")
return parser
def execute(self, parsed_args):
return self.client.records.get(parsed_args.domain_id, parsed_args.id)
class CreateRecordCommand(base.CreateCommand):
""" Create Record """
def get_parser(self, prog_name):
parser = super(CreateRecordCommand, self).get_parser(prog_name)
parser.add_argument('domain_id', help="Domain ID")
parser.add_argument('--name', help="Record Name", required=True)
parser.add_argument('--type', help="Record Type", required=True)
parser.add_argument('--data', help="Record Data", required=True)
parser.add_argument('--ttl', type=int, help="Record TTL")
return parser
def execute(self, parsed_args):
record = Record(
name=parsed_args.name,
type=parsed_args.type,
data=parsed_args.data,
ttl=parsed_args.ttl,
)
return self.client.records.create(parsed_args.domain_id, record)
class UpdateRecordCommand(base.UpdateCommand):
""" Update Record """
def get_parser(self, prog_name):
parser = super(UpdateRecordCommand, self).get_parser(prog_name)
parser.add_argument('domain_id', help="Domain ID")
parser.add_argument('id', help="Record ID")
parser.add_argument('--name', help="Record Name")
parser.add_argument('--type', help="Record Type")
parser.add_argument('--data', help="Record Data")
parser.add_argument('--ttl', type=int, help="Record TTL")
return parser
def execute(self, parsed_args):
# TODO: API needs updating.. this get is silly
record = self.client.records.get(parsed_args.domain_id, parsed_args.id)
# TODO: How do we tell if an arg was supplied or intentionally set to
# None?
record.update({
'name': parsed_args.name,
'type': parsed_args.type,
'data': parsed_args.data,
'ttl': parsed_args.ttl,
})
return self.client.records.update(parsed_args.domain_id, record)
class DeleteRecordCommand(base.DeleteCommand):
""" Delete Record """
def get_parser(self, prog_name):
parser = super(DeleteRecordCommand, self).get_parser(prog_name)
parser.add_argument('domain_id', help="Domain ID")
parser.add_argument('id', help="Record ID")
return parser
def execute(self, parsed_args):
return self.client.records.delete(parsed_args.domain_id,
parsed_args.id)

@ -0,0 +1,105 @@
# Copyright 2012 Managed I.T.
#
# Author: Kiall Mac Innes <kiall@managedit.ie>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from monikerclient.cli import base
from monikerclient.v1.servers import Server
LOG = logging.getLogger(__name__)
class ListServersCommand(base.ListCommand):
""" List Servers """
def execute(self, parsed_args):
return self.client.servers.list()
class GetServerCommand(base.GetCommand):
""" Get Server """
def get_parser(self, prog_name):
parser = super(GetServerCommand, self).get_parser(prog_name)
parser.add_argument('id', help="Server ID", required=True)
return parser
def execute(self, parsed_args):
return self.client.servers.get(parsed_args.id)
class CreateServerCommand(base.CreateCommand):
""" Create Server """
def get_parser(self, prog_name):
parser = super(CreateServerCommand, self).get_parser(prog_name)
parser.add_argument('--name', help="Server Name", required=True)
parser.add_argument('--ipv4', help="Server IPv4 Address")
parser.add_argument('--ipv6', help="Server IPv6 Address")
return parser
def execute(self, parsed_args):
server = Server(
name=parsed_args.name,
ipv4=parsed_args.ipv4,
ipv6=parsed_args.ipv6,
)
return self.client.servers.create(server)
class UpdateServerCommand(base.UpdateCommand):
""" Update Server """
def get_parser(self, prog_name):
parser = super(UpdateServerCommand, self).get_parser(prog_name)
parser.add_argument('id', help="Server ID", required=True)
parser.add_argument('--name', help="Server Name")
parser.add_argument('--ipv4', help="Server IPv4 Address")
parser.add_argument('--ipv6', help="Server IPv6 Address")
return parser
def execute(self, parsed_args):
# TODO: API needs updating.. this get is silly
server = self.client.servers.get(parsed_args.id)
# TODO: How do we tell if an arg was supplied or intentionally set to
# None?
server.update({
'name': parsed_args.name,
'ipv4': parsed_args.ipv4,
'ipv6': parsed_args.ipv6,
})
return self.client.servers.update(server)
class DeleteServerCommand(base.DeleteCommand):
""" Delete Server """
def get_parser(self, prog_name):
parser = super(DeleteServerCommand, self).get_parser(prog_name)
parser.add_argument('id', help="Server ID", required=True)
return parser
def execute(self, parsed_args):
return self.client.servers.delete(parsed_args.id)

@ -16,8 +16,8 @@
"name": {
"type": "string",
"description": "Domain name",
"format": "host-name",
"maxLength": 255,
"pattern": "^.+[^\\.]$",
"required": true
},
"email": {
@ -43,10 +43,20 @@
"readonly": true
},
"updated_at": {
"type": "string",
"type": ["string", "null"],
"description": "Date and time of image registration",
"format": "date-time",
"readonly": true
}
}
},
"links": [{
"rel": "self",
"href": "/domains/{id}"
}, {
"rel": "records",
"href": "/domains/{id}/records"
}, {
"rel": "collection",
"href": "/domains"
}]
}

@ -13,17 +13,24 @@
"pattern": "^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}$",
"readonly": true
},
"domain_id": {
"type": "string",
"description": "Domain Identifier",
"pattern": "^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}$",
"readonly": true
},
"name": {
"type": "string",
"description": "DNS Record Name",
"format": "host-name",
"maxLength": 255,
"pattern": "^.+[^\\.]$",
"required": true
},
"type": {
"type": "string",
"description": "DNS Record Type",
"enum": ["A", "AAAA", "CNAME", "MX", "SRV", "TXT", "SPF", "NS"]
"enum": ["A", "AAAA", "CNAME", "MX", "SRV", "TXT", "SPF", "NS"],
"required": true
},
"data": {
"type": "string",
@ -31,8 +38,14 @@
"maxLength": 255,
"required": true
},
"priority": {
"type": ["integer", "null"],
"description": "DNS Record Priority",
"min": 0,
"max": 99999
},
"ttl": {
"type": "integer",
"type": ["integer", "null"],
"description": "Time to live",
"min": 60
},
@ -43,10 +56,131 @@
"readonly": true
},
"updated_at": {
"type": "string",
"type": ["string", "null"],
"description": "Date and time of image registration",
"format": "date-time",
"readonly": true
}
}
},
"oneOf": [{
"description": "An A Record",
"properties": {
"type": {
"type": "string",
"enum": ["A"]
},
"data": {
"format": "ip-address",
"required": true
},
"priority": {
"type": "null"
}
}
}, {
"description": "An AAAA Record",
"properties": {
"type": {
"type": "string",
"enum": ["AAAA"]
},
"data": {
"format": "ipv6",
"required": true
},
"priority": {
"type": "null"
}
}
}, {
"description": "A CNAME Record",
"properties": {
"type": {
"type": "string",
"enum": ["CNAME"]
},
"data": {
"format": "host-name",
"required": true
},
"priority": {
"type": "null"
}
}
}, {
"description": "A MX Record",
"properties": {
"type": {
"type": "string",
"enum": ["MX"]
},
"data": {
"format": "host-name",
"required": true
},
"priority": {
"type": "integer",
"required": true
}
}
}, {
"description": "A SRV Record",
"properties": {
"type": {
"type": "string",
"enum": ["SRV"]
},
"priority": {
"type": "integer",
"required": true
}
}
}, {
"description": "A TXT Record",
"properties": {
"type": {
"type": "string",
"enum": ["TXT"]
},
"priority": {
"type": "null"
}
}
}, {
"description": "A SPF Record",
"properties": {
"type": {
"type": "string",
"enum": ["SPF"]
},
"priority": {
"type": "null"
}
}
}, {
"description": "A NS Record",
"properties": {
"type": {
"type": "string",
"enum": ["NS"]
},
"data": {
"format": "host-name",
"required": true
},
"priority": {
"type": "null"
}
}
}],
"links": [{
"rel": "self",
"href": "/domains/{domain_id}/records/{id}"
}, {
"rel": "domain",
"href": "/domains/{domain_id}"
}, {
"rel": "collection",
"href": "/domains/{domain_id}/records"
}]
}

@ -16,15 +16,14 @@
"name": {
"type": "string",
"description": "Server DNS name",
"format": "host-name",
"maxLength": 255,
"pattern": "^.+[^\\.]$",
"required": true
},
"ipv4": {
"type": "string",
"description": "IPv4 address of server",
"format": "ip-address",
"required": true
"format": "ip-address"
},
"ipv6": {
"type": "string",
@ -38,10 +37,32 @@
"readonly": true
},
"updated_at": {
"type": "string",
"type": ["string", "null"],
"description": "Date and time of last server update",
"format": "date-time",
"readonly": true
}
}
},
"anyOf": [{
"description": "IPv4",
"properties": {
"ipv4": {
"required": true
}
}
}, {
"description": "IPv6",
"properties": {
"ipv6": {
"required": true
}
}
}],
"links": [{
"rel": "self",
"href": "/servers/{id}"
}, {
"rel": "collection",
"href": "/servers"
}]
}

@ -17,63 +17,97 @@ import json
from monikerclient import warlock
from monikerclient import utils
from monikerclient.v1.base import Controller
from monikerclient.v1.domains import Domain
Record = warlock.model_factory(utils.load_schema('v1', 'record'))
class RecordsController(Controller):
def list(self):
def list(self, domain):
"""
Retrieve a list of records
:param domain: :class:`Domain` or Domain Identifier
:returns: A list of :class:`Record`s
"""
response = self.client.get('/records')
domain_id = domain.id if isinstance(domain, Domain) else domain
response = self.client.get('/domains/%(domain_id)s/records' % {
'domain_id': domain_id
})
return [Record(i) for i in response.json['records']]
def get(self, record_id):
def get(self, domain, record_id):
"""
Retrieve a record
:param domain: :class:`Domain` or Domain Identifier
:param record_id: Record Identifier
:returns: :class:`Record`
"""
response = self.client.get('/records/%s' % record_id)
domain_id = domain.id if isinstance(domain, Domain) else domain
uri = '/domains/%(domain_id)s/records/%(record_id)s' % {
'domain_id': domain_id,
'record_id': record_id
}
response = self.client.get(uri)
return Record(response.json)
def create(self, record):
def create(self, domain, record):
"""
Create a record
:param domain: :class:`Domain` or Domain Identifier
:param record: A :class:`Record` to create
:returns: :class:`Record`
"""
response = self.client.post('/records', data=json.dumps(record))
domain_id = domain.id if isinstance(domain, Domain) else domain
return record.update(response.json)
uri = '/domains/%(domain_id)s/records' % {
'domain_id': domain_id
}
def update(self, record):
response = self.client.post(uri, data=json.dumps(record))
return Record(response.json)
def update(self, domain, record):
"""
Update a record
:param domain: :class:`Domain` or Domain Identifier
:param record: A :class:`Record` to update
:returns: :class:`Record`
"""
response = self.client.put('/records/%s' % record.id,
data=json.dumps(record))
domain_id = domain.id if isinstance(domain, Domain) else domain
uri = '/domains/%(domain_id)s/records/%(record_id)s' % {
'domain_id': domain_id,
'record_id': record.id
}
response = self.client.put(uri, data=json.dumps(record))
return record.update(response.json)
def delete(self, record):
def delete(self, domain, record):
"""
Delete a record
:param domain: :class:`Domain` or Domain Identifier
:param record: A :class:`Record`, or Record Identifier to delete
"""
if isinstance(record, Record):
self.client.delete('/records/%s' % record.id)
else:
self.client.delete('/records/%s' % record)
domain_id = domain.id if isinstance(domain, Domain) else domain
record_id = record.id if isinstance(record, Record) else record
uri = '/domains/%(domain_id)s/records/%(record_id)s' % {
'domain_id': domain_id,
'record_id': record_id
}
self.client.delete(uri)

@ -53,7 +53,7 @@ class ServersController(Controller):
"""
response = self.client.post('/servers', data=json.dumps(server))
return server.update(response.json)
return Server(response.json)
def update(self, server):
"""

@ -16,8 +16,11 @@
# Hopefully we can upstream the changes ASAP.
#
import copy
import logging
import jsonschema
LOG = logging.getLogger(__name__)
class InvalidOperation(RuntimeError):
pass
@ -38,8 +41,8 @@ def model_factory(schema):
"""Apply a JSON schema to an object"""
try:
jsonschema.validate(obj, schema)
except jsonschema.ValidationError:
raise ValidationError()
except jsonschema.ValidationError, e:
raise ValidationError(str(e))
class Model(dict):
"""Self-validating model for arbitrary objects"""
@ -51,8 +54,8 @@ def model_factory(schema):
self.__dict__['validator'] = validator
try:
self.validator(d)
except ValidationError:
raise ValueError()
except ValidationError, e:
raise ValueError('Validation Error: %s' % str(e))
else:
dict.__init__(self, d)

@ -54,6 +54,18 @@ setup(
domain-create = monikerclient.cli.domains:CreateDomainCommand
domain-update = monikerclient.cli.domains:UpdateDomainCommand
domain-delete = monikerclient.cli.domains:DeleteDomainCommand
record-list = monikerclient.cli.records:ListRecordsCommand
record-get = monikerclient.cli.records:GetRecordCommand
record-create = monikerclient.cli.records:CreateRecordCommand
record-update = monikerclient.cli.records:UpdateRecordCommand
record-delete = monikerclient.cli.records:DeleteRecordCommand
server-list = monikerclient.cli.servers:ListServersCommand
server-get = monikerclient.cli.servers:GetServerCommand
server-create = monikerclient.cli.servers:CreateServerCommand
server-update = monikerclient.cli.servers:UpdateServerCommand
server-delete = monikerclient.cli.servers:DeleteServerCommand
"""),
classifiers=[
'Development Status :: 3 - Alpha',