Implemented type args in config schema
Added 'enum' and 'regex' types Update ConfigSchema to support iteration, len and getitem Added ProcessResource to model Implemented permission saving for FileResources Added new base class for all model resources Renamed Host model to HostResource Added DirectoryResource; implemented discovery for files and directories Needed for blueprint: https://blueprints.launchpad.net/rubick/+spec/python33-support Change-Id: I41f67db38ee3894aa8cf3d6e51991b027dc20485
This commit is contained in:
parent
aae261e036
commit
46c5ad5406
@ -11,6 +11,7 @@ assert rubick.inspections
|
||||
import rubick.schemas
|
||||
assert rubick.schemas
|
||||
from rubick.json import openstack_for_json
|
||||
from rubick.model import DirectoryResource
|
||||
|
||||
|
||||
def indent_prefix(indent=0):
|
||||
@ -50,8 +51,16 @@ def print_issues(issues, indent=0):
|
||||
|
||||
|
||||
def print_service(service):
|
||||
print(' ' + str(service))
|
||||
print_issues(service.all_issues, indent=2)
|
||||
print(' ' + service.name)
|
||||
print_issues(service.all_issues, indent=3)
|
||||
|
||||
|
||||
def print_path(path):
|
||||
if isinstance(path, DirectoryResource):
|
||||
print(' ' + path.path + '/')
|
||||
else:
|
||||
print(' ' + path.path)
|
||||
print_issues(path.all_issues, indent=3)
|
||||
|
||||
|
||||
def print_host(host):
|
||||
@ -59,9 +68,16 @@ def print_host(host):
|
||||
|
||||
print_issues(host.issues, indent=1)
|
||||
|
||||
print(' Services:')
|
||||
|
||||
for service in host.components:
|
||||
print_service(service)
|
||||
|
||||
print(' Filesystem:')
|
||||
|
||||
for path in sorted(host.filesystem.values(), key=lambda f: f.path):
|
||||
print_path(path)
|
||||
|
||||
|
||||
def print_openstack(openstack):
|
||||
print_issues(openstack.issues)
|
||||
@ -90,8 +106,8 @@ def main():
|
||||
x = inspection()
|
||||
x.inspect(openstack)
|
||||
|
||||
# print_openstack(openstack)
|
||||
print(json.dumps(openstack_for_json(openstack)))
|
||||
print_openstack(openstack)
|
||||
# print(json.dumps(openstack_for_json(openstack)))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -132,7 +132,7 @@ class Configuration(object):
|
||||
if param_schema:
|
||||
type_validator = TypeValidatorRegistry.get_validator(
|
||||
param_schema.type)
|
||||
type_validation_result = type_validator.validate(value)
|
||||
type_validation_result = type_validator.validate(value, **param_schema.type_args)
|
||||
if not isinstance(type_validation_result, InvalidValueError):
|
||||
value = type_validation_result
|
||||
|
||||
@ -159,7 +159,7 @@ class Configuration(object):
|
||||
|
||||
type_validator = TypeValidatorRegistry.get_validator(
|
||||
param_schema.type)
|
||||
type_validation_result = type_validator.validate(value)
|
||||
type_validation_result = type_validator.validate(value, **param_schema.type_args)
|
||||
if not isinstance(type_validation_result, InvalidValueError):
|
||||
return None
|
||||
|
||||
|
@ -319,20 +319,62 @@ def get_host_network_addresses(client):
|
||||
return addresses
|
||||
|
||||
|
||||
def permissions_string_to_number(s):
|
||||
# TODO(someone): implement it
|
||||
return 0
|
||||
def permissions_string_to_mode(s):
|
||||
mode = 0
|
||||
|
||||
if s[0] == 'd':
|
||||
mode |= stat.S_IFDIR
|
||||
elif s[0] == 's':
|
||||
mode |= stat.S_IFSOCK
|
||||
elif s[0] == 'l':
|
||||
mode |= stat.S_IFLNK
|
||||
else:
|
||||
mode |= stat.S_IFREG
|
||||
|
||||
if s[1] == 'r':
|
||||
mode |= stat.S_IRUSR
|
||||
if s[2] == 'w':
|
||||
mode |= stat.S_IWUSR
|
||||
if s[3] == 'x':
|
||||
mode |= stat.S_IXUSR
|
||||
if s[4] == 'r':
|
||||
mode |= stat.S_IRGRP
|
||||
if s[5] == 'w':
|
||||
mode |= stat.S_IWGRP
|
||||
if s[6] == 'x':
|
||||
mode |= stat.S_IXGRP
|
||||
if s[7] == 'r':
|
||||
mode |= stat.S_IROTH
|
||||
if s[8] == 'w':
|
||||
mode |= stat.S_IWOTH
|
||||
if s[9] == 'x':
|
||||
mode |= stat.S_IXOTH
|
||||
|
||||
return mode
|
||||
|
||||
|
||||
def collect_process(client, process_info):
|
||||
result = client.run(['readlink', '/proc/%d/cwd' % process_info.pid])
|
||||
cwd = result.output.strip()
|
||||
|
||||
process = ProcessResource(
|
||||
pid=process_info.pid,
|
||||
cmdline=process_info.command,
|
||||
cwd=cwd)
|
||||
process.listen_sockets = get_process_listen_sockets(client, process.pid)
|
||||
|
||||
return process
|
||||
|
||||
|
||||
def collect_file(client, path):
|
||||
ls = client.run(['ls', '-l', '--time-style=full-iso', path])
|
||||
ls = client.run(['ls', '-ld', '--time-style=full-iso', path])
|
||||
if ls.return_code != 0:
|
||||
return None
|
||||
|
||||
line = ls.output.split("\n")[0]
|
||||
perm, links, owner, group, size, date, time, timezone, name = \
|
||||
line.split()
|
||||
permissions = permissions_string_to_number(perm)
|
||||
permissions = permissions_string_to_mode(perm)
|
||||
|
||||
with client.open(path) as f:
|
||||
contents = f.read()
|
||||
@ -340,6 +382,19 @@ def collect_file(client, path):
|
||||
return FileResource(path, contents, owner, group, permissions)
|
||||
|
||||
|
||||
def collect_directory(client, path):
|
||||
ls = client.run(['ls', '-ld', '--time-style=full-iso', path])
|
||||
if ls.return_code != 0:
|
||||
return None
|
||||
|
||||
line = ls.output.split("\n")[0]
|
||||
perm, links, owner, group, size, date, time, timezone, name = \
|
||||
line.split()
|
||||
permissions = permissions_string_to_mode(perm)
|
||||
|
||||
return DirectoryResource(path, owner, group, permissions)
|
||||
|
||||
|
||||
def collect_component_configs(client, component,
|
||||
command, default_config=None):
|
||||
config_files = []
|
||||
@ -399,7 +454,7 @@ class HostDiscovery(BaseDiscovery):
|
||||
|
||||
hostname = client.run(['hostname']).output.strip()
|
||||
|
||||
item = Host(name=hostname)
|
||||
item = HostResource(name=hostname)
|
||||
item.id = get_host_id(client)
|
||||
item.network_addresses = get_host_network_addresses(client)
|
||||
|
||||
@ -447,15 +502,70 @@ class HostDiscovery(BaseDiscovery):
|
||||
return item is not None
|
||||
|
||||
|
||||
class FileSystemDiscovery(BaseDiscovery):
|
||||
|
||||
def seen(self, driver, host, path=None, **data):
|
||||
if not path:
|
||||
return True
|
||||
|
||||
client = driver.client(host)
|
||||
host_id = get_host_id(client)
|
||||
|
||||
item = find(self._seen_items,
|
||||
lambda f: f.path == path and f.host_id == host_id)
|
||||
|
||||
return item is not None
|
||||
|
||||
|
||||
class FileDiscovery(FileSystemDiscovery):
|
||||
item_type = 'file'
|
||||
|
||||
def discover(self, driver, host, path=None, **data):
|
||||
client = driver.client(host)
|
||||
|
||||
if not path:
|
||||
return None
|
||||
|
||||
item = collect_file(client, path)
|
||||
if not item:
|
||||
return None
|
||||
|
||||
item.host_id = get_host_id(client)
|
||||
|
||||
self._seen_items.append(item)
|
||||
|
||||
return item
|
||||
|
||||
|
||||
class DirectoryDiscovery(FileSystemDiscovery):
|
||||
item_type = 'directory'
|
||||
|
||||
def discover(self, driver, host, path=None, **data):
|
||||
client = driver.client(host)
|
||||
|
||||
if not path:
|
||||
return None
|
||||
|
||||
item = collect_directory(client, path)
|
||||
if not item:
|
||||
return None
|
||||
|
||||
item.host_id = get_host_id(client)
|
||||
|
||||
self._seen_items.append(item)
|
||||
|
||||
return item
|
||||
|
||||
|
||||
class ServiceDiscovery(BaseDiscovery):
|
||||
|
||||
def seen(self, driver, host, **data):
|
||||
if 'sockets' in data:
|
||||
item = find(self._seen_items,
|
||||
lambda s: data['sockets'] == s.listen_sockets)
|
||||
lambda s: data['sockets'] == s.process.listen_sockets)
|
||||
elif 'port' in data:
|
||||
item = find(self._seen_items,
|
||||
lambda s: (host, data['port']) in s.listen_sockets)
|
||||
lambda s: (host, data['port']) in s.process.listen_sockets)
|
||||
else:
|
||||
client = driver.client(host)
|
||||
host_id = client.get_host_id()
|
||||
@ -476,8 +586,9 @@ class KeystoneDiscovery(ServiceDiscovery):
|
||||
|
||||
keystone = KeystoneComponent()
|
||||
keystone.host_id = get_host_id(client)
|
||||
keystone.listen_sockets = get_process_listen_sockets(client,
|
||||
process.pid)
|
||||
|
||||
keystone.process = collect_process(client, process)
|
||||
|
||||
keystone.version = find_python_package_version(client, 'keystone')
|
||||
keystone.config_files = []
|
||||
|
||||
@ -556,8 +667,9 @@ class NovaApiDiscovery(ServiceDiscovery):
|
||||
|
||||
nova_api = NovaApiComponent()
|
||||
nova_api.host_id = get_host_id(client)
|
||||
nova_api.listen_sockets = get_process_listen_sockets(client,
|
||||
process.pid)
|
||||
|
||||
nova_api.process = collect_process(client, process)
|
||||
|
||||
nova_api.version = find_python_package_version(client, 'nova')
|
||||
|
||||
collect_component_configs(
|
||||
@ -568,6 +680,14 @@ class NovaApiDiscovery(ServiceDiscovery):
|
||||
if len(nova_api.config_files) > 0:
|
||||
config_dir = os.path.dirname(nova_api.config_files[0].path)
|
||||
|
||||
for param in nova_api.config.schema:
|
||||
if param.type == 'file':
|
||||
path = nova_api.config[param.name]
|
||||
driver.enqueue('file', host=host, path=path)
|
||||
elif param.type == 'directory':
|
||||
path = nova_api.config[param.name]
|
||||
driver.enqueue('directory', host=host, path=path)
|
||||
|
||||
paste_config_path = path_relative_to(
|
||||
nova_api.config['api_paste_config'], config_dir)
|
||||
nova_api.paste_config_file = collect_file(
|
||||
@ -590,8 +710,9 @@ class NovaComputeDiscovery(ServiceDiscovery):
|
||||
|
||||
nova_compute = NovaComputeComponent()
|
||||
nova_compute.host_id = get_host_id(client)
|
||||
nova_compute.listen_sockets = get_process_listen_sockets(client,
|
||||
process.pid)
|
||||
|
||||
nova_compute.process = collect_process(client, process)
|
||||
|
||||
nova_compute.version = find_python_package_version(client, 'nova')
|
||||
|
||||
collect_component_configs(
|
||||
@ -615,8 +736,9 @@ class NovaSchedulerDiscovery(ServiceDiscovery):
|
||||
|
||||
nova_scheduler = NovaSchedulerComponent()
|
||||
nova_scheduler.host_id = get_host_id(client)
|
||||
nova_scheduler.listen_sockets = get_process_listen_sockets(client,
|
||||
process.pid)
|
||||
|
||||
nova_scheduler.process = collect_process(client, process)
|
||||
|
||||
nova_scheduler.version = find_python_package_version(client, 'nova')
|
||||
|
||||
collect_component_configs(
|
||||
@ -640,8 +762,9 @@ class GlanceApiDiscovery(ServiceDiscovery):
|
||||
|
||||
glance_api = GlanceApiComponent()
|
||||
glance_api.host_id = get_host_id(client)
|
||||
glance_api.listen_sockets = get_process_listen_sockets(client,
|
||||
process.pid)
|
||||
|
||||
glance_api.process = collect_process(client, process)
|
||||
|
||||
glance_api.version = find_python_package_version(client, 'glance')
|
||||
|
||||
collect_component_configs(
|
||||
@ -665,8 +788,9 @@ class GlanceRegistryDiscovery(ServiceDiscovery):
|
||||
|
||||
glance_registry = GlanceRegistryComponent()
|
||||
glance_registry.host_id = get_host_id(client)
|
||||
glance_registry.listen_sockets = get_process_listen_sockets(
|
||||
client, process.pid)
|
||||
|
||||
glance_registry.process = collect_process(client, process)
|
||||
|
||||
glance_registry.version = find_python_package_version(client, 'glance')
|
||||
|
||||
collect_component_configs(
|
||||
@ -690,8 +814,9 @@ class CinderApiDiscovery(ServiceDiscovery):
|
||||
|
||||
cinder_api = CinderApiComponent()
|
||||
cinder_api.host_id = get_host_id(client)
|
||||
cinder_api.listen_sockets = get_process_listen_sockets(client,
|
||||
process.pid)
|
||||
|
||||
cinder_api.process = collect_process(client, process)
|
||||
|
||||
cinder_api.version = find_python_package_version(client, 'cinder')
|
||||
|
||||
collect_component_configs(
|
||||
@ -724,8 +849,9 @@ class CinderVolumeDiscovery(ServiceDiscovery):
|
||||
|
||||
cinder_volume = CinderVolumeComponent()
|
||||
cinder_volume.host_id = get_host_id(client)
|
||||
cinder_volume.listen_sockets = get_process_listen_sockets(client,
|
||||
process.pid)
|
||||
|
||||
cinder_volume.process = collect_process(client, process)
|
||||
|
||||
cinder_volume.version = find_python_package_version(client, 'cinder')
|
||||
|
||||
collect_component_configs(
|
||||
@ -758,8 +884,9 @@ class CinderSchedulerDiscovery(ServiceDiscovery):
|
||||
|
||||
cinder_scheduler = CinderSchedulerComponent()
|
||||
cinder_scheduler.host_id = get_host_id(client)
|
||||
cinder_scheduler.listen_sockets = get_process_listen_sockets(
|
||||
client, process.pid)
|
||||
|
||||
cinder_scheduler.process = collect_process(client, process)
|
||||
|
||||
cinder_scheduler.version = find_python_package_version(client,
|
||||
'cinder')
|
||||
|
||||
@ -786,7 +913,8 @@ class MysqlDiscovery(ServiceDiscovery):
|
||||
|
||||
mysql = MysqlComponent()
|
||||
mysql.host_id = get_host_id(client)
|
||||
mysql.listen_sockets = get_process_listen_sockets(client, process.pid)
|
||||
|
||||
mysql.process = collect_process(client, process)
|
||||
|
||||
version_result = client.run(['mysqld', '--version'])
|
||||
m = mysqld_version_re.match(version_result.output)
|
||||
@ -826,8 +954,9 @@ class RabbitmqDiscovery(ServiceDiscovery):
|
||||
|
||||
rabbitmq = RabbitMqComponent()
|
||||
rabbitmq.host_id = get_host_id(client)
|
||||
rabbitmq.listen_sockets = get_process_listen_sockets(client,
|
||||
process.pid)
|
||||
|
||||
rabbitmq.process = collect_process(client, process)
|
||||
|
||||
rabbitmq.version = 'unknown'
|
||||
|
||||
env_file = '/etc/rabbitmq/rabbitmq-env.conf'
|
||||
@ -866,8 +995,9 @@ class SwiftProxyServerDiscovery(ServiceDiscovery):
|
||||
|
||||
swift_proxy_server = SwiftProxyServerComponent()
|
||||
swift_proxy_server.host_id = get_host_id(client)
|
||||
swift_proxy_server.listen_sockets = get_process_listen_sockets(
|
||||
client, process.pid)
|
||||
|
||||
swift_proxy_server.process = collect_process(client, process)
|
||||
|
||||
swift_proxy_server.version = find_python_package_version(client,
|
||||
'swift')
|
||||
|
||||
@ -892,8 +1022,9 @@ class SwiftContainerServerDiscovery(ServiceDiscovery):
|
||||
|
||||
swift_container_server = SwiftContainerServerComponent()
|
||||
swift_container_server.host_id = get_host_id(client)
|
||||
swift_container_server.listen_sockets = get_process_listen_sockets(
|
||||
client, process.pid)
|
||||
|
||||
swift_container_server.process = collect_process(client, process)
|
||||
|
||||
swift_container_server.version = find_python_package_version(client,
|
||||
'swift')
|
||||
|
||||
@ -918,8 +1049,9 @@ class SwiftAccountServerDiscovery(ServiceDiscovery):
|
||||
|
||||
swift_account_server = SwiftAccountServerComponent()
|
||||
swift_account_server.host_id = get_host_id(client)
|
||||
swift_account_server.listen_sockets = get_process_listen_sockets(
|
||||
client, process.pid)
|
||||
|
||||
swift_account_server.process = collect_process(client, process)
|
||||
|
||||
swift_account_server.version = find_python_package_version(client,
|
||||
'swift')
|
||||
|
||||
@ -944,8 +1076,9 @@ class SwiftObjectServerDiscovery(ServiceDiscovery):
|
||||
|
||||
swift_object_server = SwiftObjectServerComponent()
|
||||
swift_object_server.host_id = get_host_id(client)
|
||||
swift_object_server.listen_sockets = get_process_listen_sockets(
|
||||
client, process.pid)
|
||||
|
||||
swift_object_server.process = collect_process(client, process)
|
||||
|
||||
swift_object_server.version = find_python_package_version(client,
|
||||
'swift')
|
||||
|
||||
@ -970,8 +1103,9 @@ class NeutronServerDiscovery(ServiceDiscovery):
|
||||
|
||||
neutron_server = NeutronServerComponent()
|
||||
neutron_server.host_id = get_host_id(client)
|
||||
neutron_server.listen_sockets = get_process_listen_sockets(client,
|
||||
process.pid)
|
||||
|
||||
neutron_server.process = collect_process(client, process)
|
||||
|
||||
neutron_server.version = find_python_package_version(client, 'neutron')
|
||||
|
||||
collect_component_configs(
|
||||
@ -995,8 +1129,9 @@ class NeutronDhcpAgentDiscovery(ServiceDiscovery):
|
||||
|
||||
neutron_dhcp_agent = NeutronDhcpAgentComponent()
|
||||
neutron_dhcp_agent.host_id = get_host_id(client)
|
||||
neutron_dhcp_agent.listen_sockets = get_process_listen_sockets(
|
||||
client, process.pid)
|
||||
|
||||
neutron_dhcp_agent.process = collect_process(client, process)
|
||||
|
||||
neutron_dhcp_agent.version = find_python_package_version(client,
|
||||
'neutron')
|
||||
|
||||
@ -1021,8 +1156,9 @@ class NeutronL3AgentDiscovery(ServiceDiscovery):
|
||||
|
||||
neutron_l3_agent = NeutronL3AgentComponent()
|
||||
neutron_l3_agent.host_id = get_host_id(client)
|
||||
neutron_l3_agent.listen_sockets = get_process_listen_sockets(
|
||||
client, process.pid)
|
||||
|
||||
neutron_l3_agent.process = collect_process(client, process)
|
||||
|
||||
neutron_l3_agent.version = find_python_package_version(client,
|
||||
'neutron')
|
||||
|
||||
@ -1047,8 +1183,9 @@ class NeutronMetadataAgentDiscovery(ServiceDiscovery):
|
||||
|
||||
neutron_metadata_agent = NeutronMetadataAgentComponent()
|
||||
neutron_metadata_agent.host_id = get_host_id(client)
|
||||
neutron_metadata_agent.listen_sockets = get_process_listen_sockets(
|
||||
client, process.pid)
|
||||
|
||||
neutron_metadata_agent.process = collect_process(client, process)
|
||||
|
||||
neutron_metadata_agent.version = find_python_package_version(client,
|
||||
'neutron')
|
||||
|
||||
@ -1073,8 +1210,9 @@ class NeutronOpenvswitchAgentDiscovery(ServiceDiscovery):
|
||||
|
||||
neutron_openvswitch_agent = NeutronOpenvswitchAgentComponent()
|
||||
neutron_openvswitch_agent.host_id = get_host_id(client)
|
||||
neutron_openvswitch_agent.listen_sockets = get_process_listen_sockets(
|
||||
client, process.pid)
|
||||
|
||||
neutron_openvswitch_agent.process = collect_process(client, process)
|
||||
|
||||
neutron_openvswitch_agent.version = find_python_package_version(
|
||||
client, 'neutron')
|
||||
|
||||
@ -1164,14 +1302,25 @@ class OpenstackDiscovery(object):
|
||||
# Rebuild model tree
|
||||
openstack = Openstack()
|
||||
|
||||
for host in filter(lambda i: isinstance(i, Host), items):
|
||||
for host in filter(lambda i: isinstance(i, HostResource), items):
|
||||
openstack.add_host(host)
|
||||
|
||||
for service in filter(lambda i: isinstance(i, Service), items):
|
||||
host = find(openstack.hosts, lambda h: h.id == service.host_id)
|
||||
if not host:
|
||||
logger.error('Got resource "%s" '
|
||||
'that belong to non-existing host' % service)
|
||||
continue
|
||||
|
||||
host.add_component(service)
|
||||
|
||||
for fs_resource in filter(lambda f: isinstance(f, FileSystemResource), items):
|
||||
host = find(openstack.hosts, lambda h: h.id == fs_resource.host_id)
|
||||
if not host:
|
||||
logger.error('Got resource "%s" '
|
||||
'that belong to non-existing host' % fs_resource)
|
||||
continue
|
||||
|
||||
host.add_fs_resource(fs_resource)
|
||||
|
||||
return openstack
|
||||
|
@ -23,7 +23,11 @@ class IssueReporter(object):
|
||||
return list(self.issues)
|
||||
|
||||
|
||||
class Openstack(IssueReporter):
|
||||
class Resource(IssueReporter):
|
||||
pass
|
||||
|
||||
|
||||
class Openstack(Resource):
|
||||
|
||||
def __init__(self):
|
||||
super(Openstack, self).__init__()
|
||||
@ -54,12 +58,13 @@ class Openstack(IssueReporter):
|
||||
return components
|
||||
|
||||
|
||||
class Host(IssueReporter):
|
||||
class HostResource(Resource):
|
||||
|
||||
def __init__(self, name):
|
||||
super(Host, self).__init__()
|
||||
super(HostResource, self).__init__()
|
||||
self.name = name
|
||||
self.components = []
|
||||
self.filesystem = {}
|
||||
|
||||
def __str__(self):
|
||||
return 'Host "%s"' % self.name
|
||||
@ -71,13 +76,20 @@ class Host(IssueReporter):
|
||||
self.components.append(component)
|
||||
component.parent = self
|
||||
|
||||
def add_fs_resource(self, resource):
|
||||
if not resource:
|
||||
return
|
||||
|
||||
self.filesystem[resource.path] = resource
|
||||
resource.parent = self
|
||||
|
||||
@property
|
||||
def openstack(self):
|
||||
return self.parent
|
||||
|
||||
@property
|
||||
def all_issues(self):
|
||||
result = super(Host, self).all_issues
|
||||
result = super(HostResource, self).all_issues
|
||||
|
||||
for component in self.components:
|
||||
result.extend(component.all_issues)
|
||||
@ -85,7 +97,16 @@ class Host(IssueReporter):
|
||||
return result
|
||||
|
||||
|
||||
class Service(IssueReporter):
|
||||
class ProcessResource(Resource):
|
||||
|
||||
def __init__(self, pid, cmdline, cwd):
|
||||
super(ProcessResource, self).__init__()
|
||||
self.pid = pid
|
||||
self.cmdline = cmdline
|
||||
self.cwd = cwd
|
||||
|
||||
|
||||
class Service(Resource):
|
||||
|
||||
def __init__(self):
|
||||
super(Service, self).__init__()
|
||||
@ -383,15 +404,34 @@ class SwiftObjectServerComponent(OpenstackComponent):
|
||||
name = 'swift-object-server'
|
||||
|
||||
|
||||
class FileResource(IssueReporter):
|
||||
|
||||
def __init__(self, path, contents, owner, group, permissions):
|
||||
super(FileResource, self).__init__()
|
||||
class FileSystemResource(Resource):
|
||||
def __init__(self, path, owner, group, permissions):
|
||||
super(FileSystemResource, self).__init__()
|
||||
self.path = path
|
||||
self.contents = contents
|
||||
self.owner = owner
|
||||
self.group = group
|
||||
self.permissions = permissions
|
||||
|
||||
def __str__(self):
|
||||
return 'File "%s"' % self.path
|
||||
return '%s "%s"' % (
|
||||
self.__class__.__name__.split('.')[-1].replace('Resource', ''),
|
||||
self.path)
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
'%s(path=%s, owner=%s, group=%s, permissions=%s)' %
|
||||
(self.__class__.__name__.split('.')[-1], repr(self.path),
|
||||
repr(self.owner), repr(self.group), repr(self.permissions))
|
||||
)
|
||||
|
||||
|
||||
class FileResource(FileSystemResource):
|
||||
|
||||
def __init__(self, path, contents, owner, group, permissions):
|
||||
super(FileResource, self).__init__(
|
||||
path, owner, group, permissions)
|
||||
self.contents = contents
|
||||
|
||||
|
||||
class DirectoryResource(FileSystemResource):
|
||||
pass
|
||||
|
@ -65,6 +65,7 @@ class ConfigSchemaRegistry:
|
||||
|
||||
param = ConfigParameterSchema(
|
||||
name, param_data['type'], section=section,
|
||||
type_args=param_data.get('type_args', {}),
|
||||
default=param_data.get('default', None),
|
||||
description=param_data.get('help', None),
|
||||
required=param_data.get('required', False),
|
||||
@ -120,6 +121,19 @@ class ConfigSchema:
|
||||
|
||||
return self._parameterByName.get(fullname, None)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.parameters)
|
||||
|
||||
def __iter__(self):
|
||||
for param in self.parameters:
|
||||
yield param
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.get_parameter(key)
|
||||
|
||||
def __contains__(self, item):
|
||||
return item in self._parameterByName
|
||||
|
||||
def __repr__(self):
|
||||
return ('<ConfigSchema name=%s version=%s format=%s parameters=%s>' %
|
||||
(self.name, self.version, self.format, self.parameters))
|
||||
@ -127,11 +141,12 @@ class ConfigSchema:
|
||||
|
||||
class ConfigParameterSchema:
|
||||
|
||||
def __init__(self, name, type, section=None, description=None,
|
||||
def __init__(self, name, type, type_args={}, section=None, description=None,
|
||||
default=None, required=False, deprecation_message=None):
|
||||
self.section = section
|
||||
self.section = section or 'DEFAULT'
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.type_args = type_args
|
||||
self.fullname = param_fullname(name, section)
|
||||
self.description = description
|
||||
self.default = default
|
||||
@ -183,10 +198,10 @@ class TypeValidator(object):
|
||||
self.base_type = base_type
|
||||
self.f = f
|
||||
|
||||
def validate(self, value):
|
||||
def validate(self, value, **kwargs):
|
||||
if value is None:
|
||||
return value
|
||||
return getattr(self, 'f')(value)
|
||||
return getattr(self, 'f')(value, **kwargs)
|
||||
|
||||
|
||||
def type_validator(name, base_type=None, default=False, **kwargs):
|
||||
@ -194,8 +209,8 @@ def type_validator(name, base_type=None, default=False, **kwargs):
|
||||
base_type = name
|
||||
|
||||
def wrap(fn):
|
||||
def wrapped(s):
|
||||
return fn(s, **kwargs)
|
||||
def wrapped(s, **immediate_kwargs):
|
||||
return fn(s, **dict(kwargs, **immediate_kwargs))
|
||||
o = TypeValidator(base_type, wrapped)
|
||||
TypeValidatorRegistry.register_validator(name, o, default=default)
|
||||
return fn
|
||||
@ -221,16 +236,19 @@ def validate_boolean(s):
|
||||
return InvalidValueError('Value should be "true" or "false"')
|
||||
|
||||
|
||||
@type_validator('enum')
|
||||
def validate_enum(s, values=[]):
|
||||
if s in values:
|
||||
return None
|
||||
if len(values) == 0:
|
||||
message = 'There should be no value'
|
||||
message = 'There should be no value, but found %s' % repr(s)
|
||||
elif len(values) == 1:
|
||||
message = 'The only valid value is %s' % values[0]
|
||||
message = 'The only valid value is "%s", but found "%s"' % (
|
||||
repr(values[0]), repr(s))
|
||||
else:
|
||||
message = 'Valid values are %s and %s' % (
|
||||
', '.join(values[:-1]), values[-1])
|
||||
message = 'Valid values are %s and %s, but found %s' % (
|
||||
', '.join([repr(v) for v in values[:-1]]),
|
||||
repr(values[-1]), repr(s))
|
||||
return InvalidValueError('%s' % message)
|
||||
|
||||
|
||||
@ -390,12 +408,22 @@ def validate_host_and_port(s, default_port=None):
|
||||
@type_validator('multi', base_type='multi')
|
||||
@type_validator('file', base_type='string')
|
||||
@type_validator('directory', base_type='string')
|
||||
@type_validator('regex', base_type='string')
|
||||
@type_validator('host_v6', base_type='string')
|
||||
def validate_string(s):
|
||||
return s
|
||||
|
||||
|
||||
@type_validator('regex', base_type='string')
|
||||
@type_validator('regexp', base_type='string')
|
||||
def validate_regex(s):
|
||||
try:
|
||||
re.compile(s)
|
||||
except re.error as e:
|
||||
return InvalidValueError(str(e))
|
||||
|
||||
return s
|
||||
|
||||
|
||||
@type_validator('integer')
|
||||
def validate_integer(s, min=None, max=None):
|
||||
if isinstance(s, int):
|
||||
|
@ -118,7 +118,8 @@ def generate_project_schema(project):
|
||||
param['type'] = prev_param['type']
|
||||
|
||||
if param.get('default', None) is not None:
|
||||
value = validator.validate(param['default'])
|
||||
type_args = param.get('type_args', {})
|
||||
value = validator.validate(param['default'], **type_args)
|
||||
if not isinstance(value, Issue):
|
||||
param['default'] = value
|
||||
else:
|
||||
@ -217,7 +218,8 @@ def generate_project_schema(project):
|
||||
param['default'] = old_param['default']
|
||||
|
||||
if param.get('default', None) is not None:
|
||||
value = validator.validate(old_param['default'])
|
||||
type_args = old_param.get('type_args', {})
|
||||
value = validator.validate(old_param['default'], **type_args)
|
||||
if not isinstance(value, Issue):
|
||||
param['default'] = value
|
||||
else:
|
||||
|
@ -9,12 +9,13 @@ class TypeValidatorTestHelper(object):
|
||||
super(TypeValidatorTestHelper, self).setUp()
|
||||
self.validator = TypeValidatorRegistry.get_validator(self.type_name)
|
||||
|
||||
def assertValid(self, value):
|
||||
self.assertNotIsInstance(self.validator.validate(value), Issue)
|
||||
def assertValid(self, value, type_args={}):
|
||||
self.assertNotIsInstance(
|
||||
self.validator.validate(value, **type_args), Issue)
|
||||
|
||||
def assertInvalid(self, value):
|
||||
def assertInvalid(self, value, type_args={}):
|
||||
self.assertIsInstance(
|
||||
self.validator.validate(value), Issue)
|
||||
self.validator.validate(value, **type_args), Issue)
|
||||
|
||||
|
||||
class StringTypeValidatorTests(TypeValidatorTestHelper, unittest.TestCase):
|
||||
@ -31,6 +32,19 @@ class StringTypeValidatorTests(TypeValidatorTestHelper, unittest.TestCase):
|
||||
self.assertEqual(s, self.validator.validate(s))
|
||||
|
||||
|
||||
class EnumTypeValidatorTests(TypeValidatorTestHelper, unittest.TestCase):
|
||||
type_name = 'enum'
|
||||
|
||||
def test_listed_value(self):
|
||||
self.assertValid('foo', type_args={'values': ['foo', 'bar']})
|
||||
|
||||
def test_unlisted_value(self):
|
||||
self.assertInvalid('baz', type_args={'values': ['foo', 'bar']})
|
||||
|
||||
def test_with_no_values_returns_error(self):
|
||||
self.assertInvalid('foo')
|
||||
|
||||
|
||||
class BooleanTypeValidatorTests(TypeValidatorTestHelper, unittest.TestCase):
|
||||
type_name = 'boolean'
|
||||
|
||||
@ -250,6 +264,16 @@ class HostAndPortTypeValidatorTests(TypeValidatorTestHelper,
|
||||
self.assertInvalid('10.0.0.1:65536')
|
||||
|
||||
|
||||
class RegexTypeValidatorTests(TypeValidatorTestHelper, unittest.TestCase):
|
||||
type_name = 'regex'
|
||||
|
||||
def test_valid_regex(self):
|
||||
self.assertValid('\d+\.\d+\.\d+\.\d+')
|
||||
|
||||
def test_invalid_regex(self):
|
||||
self.assertInvalid('(\d+')
|
||||
|
||||
|
||||
class StringListTypeValidatorTests(TypeValidatorTestHelper, unittest.TestCase):
|
||||
type_name = 'string_list'
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user