Validate each service within the bundle.

    Receive the services and machines sections of the bundle.
    Use the given add_error callable to register validation error.

def _validate_services(services, machines, add_error):
    """Validate each service within the bundle.

    Receive the services and machines sections of the bundle.
    Use the given add_error callable to register validation error.
    """
    machine_ids = set()

    for service_name, service in services.items():
        if not isstring(service_name):
            add_error('service name {} must be a string'.format(service_name))
        if service.get('expose') not in (True, False, None):
            add_error(
                'invalid expose value for service {}'.format(service_name))
        # Validate and retrieve the service charm URL and number of units.
        charm = _validate_charm(service.get('charm'), service_name, add_error)
        num_units = _validate_num_units(
            service.get('num_units'), service_name, add_error)
        # Validate service constraints and storage constraints.
        label = 'service {}'.format(service_name)
        _validate_constraints(service.get('constraints'), label, add_error)
        _validate_storage(service.get('storage'), service_name, add_error)
        # Validate service options and annotations.
        _validate_options(service.get('options'), service_name, add_error)
        _validate_annotations(service.get('annotations'), label, add_error)
        # Retrieve and validate the service units placement.
        placements = service.get('to', [])
        if not islist(placements):
            placements = [placements]
        if (num_units is not None) and (len(placements) > num_units):
            add_error(
                'too many units placed for service {}'.format(service_name))
        for placement in placements:
            machine_id = _validate_placement(
                placement, services, machines, charm, add_error)
            machine_ids.add(machine_id)

    if machines is not None:
        # Notify unused machines.
        unused = set(machines).difference(machine_ids)
        for machine_id in unused:
            add_error(
                'machine {} not referred to by a placement directive'
                ''.format(machine_id))