def network_attach(self, context, container, requested_network): with docker_utils.docker_client() as docker: security_group_ids = None if container.security_groups: security_group_ids = utils.get_security_group_ids( context, container.security_groups) network_api = zun_network.api(context, docker_api=docker) network = requested_network['network'] if network in container.addresses: raise exception.ZunException('Container %(container)s has' ' alreay connected to the network' '%(network)s.' % {'container': container.uuid, 'network': network}) self._get_or_create_docker_network(context, network_api, network) docker_net_name = self._get_docker_network_name(context, network) addrs = network_api.connect_container_to_network( container, docker_net_name, requested_network, security_groups=security_group_ids) if addrs is None: raise exception.ZunException(_( 'Unexpected missing of addresses')) update = {} update[network] = addrs addresses = container.addresses addresses.update(update) container.addresses = addresses container.save(context)
def search_image(self, context, repo, tag, exact_match): image_ref = docker_image.Reference.parse(repo) registry, image_name = image_ref.split_hostname() if registry and registry != 'docker.io': # Images searching is only supported in DockerHub msg = _('Image searching is not supported in registry: {0}') raise exception.OperationNotSupported(msg.format(registry)) with docker_utils.docker_client() as docker: try: # TODO(hongbin): search image by both name and tag images = docker.search(image_name) except errors.APIError as api_error: raise exception.ZunException(str(api_error)) except Exception as e: msg = _('Cannot search image in docker: {0}') raise exception.ZunException(msg.format(e)) if exact_match: images = [i for i in images if i['name'] == image_name] for image in images: image['metadata'] = {} for key in ('is_official', 'star_count'): value = image.pop(key, None) if value is not None: image['metadata'][key] = value # TODO(hongbin): convert images to a list of Zun Image object return images
def pull_image(self, context, repo, tag, image_pull_policy): image = self._search_image_on_host(repo, tag) if not utils.should_pull_image(image_pull_policy, bool(image)): if image: LOG.debug('Image %s present locally' % repo) return image else: message = _('Image %s not present with pull policy of Never' ) % repo raise exception.ImageNotFound(message) try: LOG.debug('Pulling image from docker %s,' ' context %s' % (repo, context)) self._pull_image(repo, tag) return {'image': repo, 'path': None} except exception.ImageNotFound: with excutils.save_and_reraise_exception(): LOG.error( 'Image %s was not found in docker repo' % repo) except exception.DockerError: with excutils.save_and_reraise_exception(): LOG.error( 'Docker API error occurred during downloading\ image %s' % repo) except errors.APIError as api_error: raise exception.ZunException(str(api_error)) except Exception as e: msg = _('Cannot download image from docker: {0}') raise exception.ZunException(msg.format(e))
def pull_image(self, context, repo, tag, image_pull_policy): # TODO(shubhams): glance driver does not handle tags # once metadata is stored in db then handle tags image_loaded = False image = self._search_image_on_host(context, repo) if image: image_path = image['path'] image_checksum = image['checksum'] md5sum = hashlib.md5() with open(image_path, 'rb') as fd: while True: # read 10MB of data each time data = fd.read(10 * 1024 * 1024) if not data: break md5sum.update(data) md5sum = md5sum.hexdigest() if md5sum == image_checksum: image_loaded = True return image, image_loaded if not common_utils.should_pull_image(image_pull_policy, bool(image)): if image: LOG.debug('Image %s present locally', repo) image_loaded = True return image, image_loaded else: message = _( 'Image %s not present with pull policy of Never') % repo raise exception.ImageNotFound(message) LOG.debug('Pulling image from glance %s', repo) try: image_meta = utils.find_image(context, repo) LOG.debug('Image %s was found in glance, downloading now...', repo) image_chunks = utils.download_image_in_chunks( context, image_meta.id) except exception.ImageNotFound: LOG.error('Image %s was not found in glance', repo) raise except Exception as e: msg = _('Cannot download image from glance: {0}') raise exception.ZunException(msg.format(e)) try: images_directory = CONF.glance.images_directory fileutils.ensure_tree(images_directory) out_path = os.path.join(images_directory, image_meta.id + '.tar') with open(out_path, 'wb') as fd: for chunk in image_chunks: fd.write(chunk) except Exception as e: msg = _('Error occurred while writing image: {0}') raise exception.ZunException(msg.format(e)) LOG.debug('Image %(repo)s was downloaded to path : %(path)s', { 'repo': repo, 'path': out_path }) return {'image': repo, 'path': out_path}, image_loaded
def get_server_id(self, server, raise_on_error=True): if uuidutils.is_uuid_like(server): return server elif isinstance(server, six.string_types): servers = self.client().servers.list(search_opts={'name': server}) if len(servers) == 1: return servers[0].id if raise_on_error: raise exception.ZunException( _("Unable to get server id with name %s") % server) else: raise exception.ZunException(_("Unexpected server type"))
def delete_image(self, context, img_id): LOG.debug('Delete an image %s in docker', img_id) with docker_utils.docker_client() as docker: try: docker.remove_image(img_id) except errors.ImageNotFound: return except errors.APIError as api_error: raise exception.ZunException(str(api_error)) except Exception as e: LOG.exception( 'Unknown exception occurred while deleting ' 'image %s in glance:%s', img_id, str(e)) raise exception.ZunException(str(e))
def _find_container_by_server_name(self, name): with docker_utils.docker_client() as docker: for info in docker.list_instances(inspect=True): if info['Config'].get('Hostname') == name: return info['Id'] raise exception.ZunException( _("Cannot find container with name %s") % name)
def create_sandbox(self, context, container, key_name=None, flavor='m1.small', image='kubernetes/pause', nics='auto'): # FIXME(hongbin): We elevate to admin privilege because the default # policy in nova disallows non-admin users to create instance in # specified host. This is not ideal because all nova instances will # be created at service admin tenant now, which breaks the # multi-tenancy model. We need to fix it. elevated = context.elevated() novaclient = nova.NovaClient(elevated) name = self.get_sandbox_name(container) if container.host != CONF.host: raise exception.ZunException( _("Host mismatch: container should be created at host '%s'.") % container.host) # NOTE(hongbin): The format of availability zone is ZONE:HOST:NODE # However, we just want to specify host, so it is ':HOST:' az = ':%s:' % container.host sandbox = novaclient.create_server(name=name, image=image, flavor=flavor, key_name=key_name, nics=nics, availability_zone=az) self._ensure_active(novaclient, sandbox) sandbox_id = self._find_container_by_server_name(name) return sandbox_id
def create_sandbox(self, context, container, requested_networks, requested_volumes, image='kubernetes/pause'): with docker_utils.docker_client() as docker: network_api = zun_network.api(context=context, docker_api=docker) self._provision_network(context, network_api, requested_networks) binds = self._get_binds(context, requested_volumes) host_config = {'binds': binds} name = self.get_sandbox_name(container) volumes = [b['bind'] for b in binds.values()] kwargs = { 'name': name, 'hostname': name[:63], 'volumes': volumes, } self._process_networking_config( context, container, requested_networks, host_config, kwargs, docker) kwargs['host_config'] = docker.create_host_config(**host_config) sandbox = docker.create_container(image, **kwargs) container.set_sandbox_id(sandbox['Id']) addresses = self._setup_network_for_container( context, container, requested_networks, network_api) if not addresses: raise exception.ZunException(_( "Unexpected missing of addresses")) container.addresses = addresses container.save(context) docker.start(sandbox['Id']) return sandbox['Id']
def _get_subnetpool(self, subnet): # NOTE(kiennt): Elevate admin privilege to list all subnetpools # across projects. admin_context = zun_context.get_admin_context() neutron_api = neutron.NeutronAPI(admin_context) subnetpool_id = subnet.get('subnetpool_id') if self._check_valid_subnetpool(neutron_api, subnetpool_id, subnet['cidr']): return subnetpool_id # NOTE(kiennt): Subnetpool which was created by Kuryr-libnetwork # will be tagged with subnet_id. kwargs = { 'tags': [subnet['id']], } subnetpools = \ neutron_api.list_subnetpools(**kwargs).get('subnetpools', []) if not subnetpools: return None elif len(subnetpools) > 1: raise exception.ZunException(_( 'Multiple Neutron subnetpools exist with prefixes %s'), subnet['cidr']) else: return subnetpools[0]['id']
def _pull_image(self, repo, tag, registry): auth_config = None image_ref = docker_image.Reference.parse(repo) registry_domain, remainder = image_ref.split_hostname() if registry and registry.username: auth_config = { 'username': registry.username, 'password': registry.password } elif (registry_domain and registry_domain == CONF.docker.default_registry and CONF.docker.default_registry_username): auth_config = { 'username': CONF.docker.default_registry_username, 'password': CONF.docker.default_registry_password } with docker_utils.docker_client() as docker: try: docker.pull(repo, tag=tag, auth_config=auth_config) except errors.NotFound as e: raise exception.ImageNotFound(message=str(e)) except errors.APIError: LOG.exception('Error on pulling image') message = _('Error on pulling image: %(repo)s:%(tag)s') % { 'repo': repo, 'tag': tag } raise exception.ZunException(message)
def test_container_run_image_pull_exception_raised( self, mock_pull, mock_attach_volume, mock_detach_volume, mock_list_by_container, mock_save, mock_spawn_n): container_dict = utils.get_test_container( image='test:latest', image_driver='docker', image_pull_policy='ifnotpresent') container = Container(self.context, **container_dict) mock_pull.side_effect = exception.ZunException( message="Image Not Found") mock_spawn_n.side_effect = lambda f, *x, **y: f(*x, **y) networks = [] volumes = [FakeVolumeMapping()] self.compute_manager.container_create( self.context, requested_networks=networks, requested_volumes=volumes, container=container, limits=None, run=True) mock_save.assert_called_with(self.context) self.assertEqual('Error', container.status) self.assertEqual('Image Not Found', container.status_reason) mock_pull.assert_called_once_with(self.context, 'test', 'latest', 'ifnotpresent', 'docker') mock_attach_volume.assert_called_once() mock_detach_volume.assert_called_once() self.assertEqual(0, len(FakeVolumeMapping.volumes))
def _make_vif_subnets(neutron_port, subnets): """Gets a list of os-vif Subnet objects for port. :param neutron_port: dict containing port information as returned by neutron client's 'show_port' :param subnets: subnet mapping :return: list of os-vif Subnet object """ vif_subnets = {} for neutron_fixed_ip in neutron_port.get('fixed_ips', []): subnet_id = neutron_fixed_ip['subnet_id'] ip_address = neutron_fixed_ip['ip_address'] if subnet_id not in subnets: continue try: subnet = vif_subnets[subnet_id] except KeyError: subnet = _make_vif_subnet(subnets, subnet_id) vif_subnets[subnet_id] = subnet subnet.ips.objects.append(osv_fixed_ip.FixedIP(address=ip_address)) if not vif_subnets: raise exception.ZunException( "No valid subnets found for port %(port_id)s" % {'port_id': neutron_port.get('id')}) return list(vif_subnets.values())
def delete_image(context, img_id, image_driver): try: image_driver.delete_image(context, img_id) except Exception as e: LOG.exception('Unknown exception occurred while deleting image %s: %s', img_id, six.text_type(e)) raise exception.ZunException(six.text_type(e))
def create_sandbox(self, context, container, image='kubernetes/pause', requested_networks=None): with docker_utils.docker_client() as docker: network_api = zun_network.api(context=context, docker_api=docker) if not requested_networks: network = self._provision_network(context, container, network_api) requested_networks = [{'network': network['Name'], 'port': '', 'v4-fixed-ip': '', 'v6-fixed-ip': ''}] name = self.get_sandbox_name(container) sandbox = docker.create_container(image, name=name, hostname=name[:63]) container.set_sandbox_id(sandbox['Id']) addresses = self._setup_network_for_container( context, container, requested_networks, network_api) if addresses is None: raise exception.ZunException(_( "Unexpected missing of addresses")) container.addresses = addresses container.save(context) docker.start(sandbox['Id']) return sandbox['Id']
def on_container_started(self, container): response = self.docker.inspect_container(container.container_id) state = response.get('State') if type(state) is dict and state.get('Pid'): pid = state['Pid'] else: pid = None # no change cni_metadata = container.cni_metadata if cni_metadata.get(consts.CNI_METADATA_PID) == pid: return # remove container from old network old_pid = cni_metadata.pop(consts.CNI_METADATA_PID, None) if old_pid: self._delete(container, old_pid) container.cni_metadata = cni_metadata container.save(self.context) # add container to network self._add(container, pid) cni_metadata[consts.CNI_METADATA_PID] = pid container.cni_metadata = cni_metadata container.save(self.context) # notify the container that network is setup cmd = ['touch', ZUN_INIT_NETWORK_READY_PATH] exit_code, output = self._exec_command_in_container(container, cmd) if exit_code != 0: raise exception.ZunException('Execute command %(cmd)s failed, ' 'output is: %(output)s' % { 'cmd': ' '.join(cmd), 'output': output })
def decorated_function(self, context, volume, **kwargs): provider = volume.volume_provider if provider not in supported_providers: msg = _("The volume provider '%s' is not supported") % provider raise exception.ZunException(msg) return function(self, context, volume, **kwargs)
def pull_image(self, context, repo, tag, image_pull_policy): image_loaded = True tag = utils.parse_tag_name(tag) image = self._search_image_on_host(repo, tag) if not utils.should_pull_image(image_pull_policy, bool(image)): if image: LOG.debug('Image %s present locally', repo) return image, image_loaded else: message = _( 'Image %s not present with pull policy of Never') % repo raise exception.ImageNotFound(message) try: LOG.debug( 'Pulling image from docker %(repo)s,' ' context %(context)s', { 'repo': repo, 'context': context }) self._pull_image(repo, tag) return {'image': repo, 'path': None}, image_loaded except exception.ImageNotFound: with excutils.save_and_reraise_exception(): LOG.error('Image %s was not found in docker repo', repo) except exception.DockerError: with excutils.save_and_reraise_exception(): LOG.error( 'Docker API error occurred during downloading ' 'image %s', repo) except Exception as e: msg = _('Cannot download image from docker: {0}') raise exception.ZunException(msg.format(e))
def search_image(self, context, repo, tag, exact_match): with docker_utils.docker_client() as docker: try: # TODO(hongbin): search image by both repo and tag images = docker.search(repo) except errors.APIError as api_error: raise exception.ZunException(str(api_error)) except Exception as e: msg = _('Cannot search image in docker: {0}') raise exception.ZunException(msg.format(e)) if exact_match: images = [i for i in images if i['name'] == repo] # TODO(hongbin): convert images to a list of Zun Image object return images
def pull_image(context, repo, tag, image_pull_policy='always', image_driver=None): if image_driver: image_driver_list = [image_driver.lower()] else: image_driver_list = CONF.image_driver_list for driver in image_driver_list: try: image_driver = load_image_driver(driver) image, image_loaded = image_driver.pull_image( context, repo, tag, image_pull_policy) if image: image['driver'] = driver.split('.')[0] break except exception.ImageNotFound: image = None except Exception as e: LOG.exception( 'Unknown exception occurred while loading ' 'image: %s', six.text_type(e)) raise exception.ZunException(six.text_type(e)) if not image: raise exception.ImageNotFound("Image %s not found" % repo) return image, image_loaded
def __init__(self, context, provider): if provider not in self.supported_providers: msg = _("Unsupported volume provider '%s'") % provider raise exception.ZunException(msg) self.context = context self.provider = provider
def upload_image_data(self, context, img_id, data): """Upload an image.""" LOG.debug('Uploading an image to glance %s', img_id) try: return utils.upload_image_data(context, img_id, data) except Exception as e: raise exception.ZunException(six.text_type(e))
def new_websocket_client(self): """Called after a new WebSocket connection has been established.""" # Reopen the eventlet hub to make sure we don't share an epoll # fd with parent and/or siblings, which would be bad from eventlet import hubs hubs.use_hub() # The zun expected behavior is to have token # passed to the method GET of the request parse = urlparse.urlparse(self.path) if parse.scheme not in ('http', 'https'): # From a bug in urlparse in Python < 2.7.4 we cannot support # special schemes (cf: https://bugs.python.org/issue9374) if sys.version_info < (2, 7, 4): raise exception.ZunException( _("We do not support scheme '%s' under Python < 2.7.4, " "please use http or https") % parse.scheme) query = parse.query token = urlparse.parse_qs(query).get("token", [""]).pop() uuid = urlparse.parse_qs(query).get("uuid", [""]).pop() exec_id = urlparse.parse_qs(query).get("exec_id", [""]).pop() ctx = context.get_admin_context(all_projects=True) if uuidutils.is_uuid_like(uuid): container = objects.Container.get_by_uuid(ctx, uuid) else: container = objects.Container.get_by_name(ctx, uuid) if exec_id: self._new_exec_client(container, token, uuid, exec_id) else: self._new_websocket_client(container, token, uuid)
def create_network(self, name, neutron_net_id): """Create a docker network with Kuryr driver. The docker network to be created will be based on the specified neutron net. It is assumed that the neutron net will have one or two subnets. If there are two subnets, it must be a ipv4 subnet and a ipv6 subnet and containers created from this network will have both ipv4 and ipv6 addresses. What this method does is finding the subnets under the specified neutron net, retrieving the cidr, gateway, subnetpool of each subnet, and compile the list of parameters for docker.create_network. """ # find a v4 and/or v6 subnet of the network subnets = self.neutron_api.list_subnets(network_id=neutron_net_id) subnets = subnets.get('subnets', []) v4_subnet = self._get_subnet(subnets, ip_version=4) v6_subnet = self._get_subnet(subnets, ip_version=6) if not v4_subnet and not v6_subnet: raise exception.ZunException(_( "The Neutron network %s has no subnet") % neutron_net_id) ipam_options = { "Driver": CONF.network.driver_name, "Options": {}, "Config": [] } if v4_subnet: ipam_options["Options"]['neutron.pool.uuid'] = ( v4_subnet.get('subnetpool_id')) ipam_options["Config"].append({ "Subnet": v4_subnet['cidr'], "Gateway": v4_subnet['gateway_ip'] }) if v6_subnet: ipam_options["Options"]['neutron.pool.v6.uuid'] = ( v6_subnet.get('subnetpool_id')) ipam_options["Config"].append({ "Subnet": v6_subnet['cidr'], "Gateway": v6_subnet['gateway_ip'] }) options = { 'neutron.net.uuid': neutron_net_id } if v4_subnet: options['neutron.pool.uuid'] = v4_subnet.get('subnetpool_id') if v6_subnet: options['neutron.pool.v6.uuid'] = v6_subnet.get('subnetpool_id') LOG.debug("Calling docker.create_network to create network %s, " "ipam_options %s, options %s", name, ipam_options, options) docker_network = self.docker.create_network( name=name, driver=CONF.network.driver_name, enable_ipv6=True if v6_subnet else False, options=options, ipam=ipam_options) return docker_network
def create_image(self, context, image_name, image_driver): try: img = image_driver.create_image(context, image_name) except Exception as e: LOG.exception('Unknown exception occurred while creating ' 'image: %s', six.text_type(e)) raise exception.ZunException(six.text_type(e)) return img
def _get_volume_driver(self, volume_mapping): driver_name = volume_mapping.volume_provider driver = self.volume_drivers.get(driver_name) if not driver: msg = _("The volume provider '%s' is not supported") % driver_name raise exception.ZunException(msg) return driver
def create_image(self, context, image_name): """Create an image.""" LOG.debug('Creating a new image in glance %s', image_name) try: # Return a created image return utils.create_image(context, image_name) except Exception as e: raise exception.ZunException(six.text_type(e))
def pull_image(self, context, repo, tag, image_pull_policy, registry): image_loaded = False image = self._search_image_on_host(context, repo, tag) if not common_utils.should_pull_image(image_pull_policy, bool(image)): if image: if self._verify_md5sum_for_image(image): image_loaded = True return image, image_loaded else: message = _( 'Image %s not present with pull policy of Never') % repo raise exception.ImageNotFound(message) LOG.debug('Pulling image from glance %s', repo) try: image_meta = utils.find_image(context, repo, tag) LOG.debug('Image %s was found in glance, downloading now...', repo) image_chunks = utils.download_image_in_chunks( context, image_meta.id) except exception.ImageNotFound: LOG.error('Image %s was not found in glance', repo) raise except Exception as e: msg = _('Cannot download image from glance: {0}') raise exception.ZunException(msg.format(e)) try: images_directory = CONF.glance.images_directory fileutils.ensure_tree(images_directory) out_path = os.path.join(images_directory, image_meta.id + '.tar') with open(out_path, 'wb') as fd: for chunk in image_chunks: fd.write(chunk) except Exception as e: msg = _('Error occurred while writing image: {0}') raise exception.ZunException(msg.format(e)) LOG.debug('Image %(repo)s was downloaded to path : %(path)s', { 'repo': repo, 'path': out_path }) image = { 'image': image_meta.name, 'tags': image_meta.tags, 'path': out_path } return image, image_loaded
def test_container_create_pull_image_failed_zun_exception( self, mock_fail, mock_pull, mock_create_sandbox, mock_save): container = Container(self.context, **utils.get_test_container()) mock_pull.side_effect = exception.ZunException( message="Image Not Found") mock_create_sandbox.return_value = mock.MagicMock() self.compute_manager._do_container_create(self.context, container) mock_fail.assert_called_once_with(self.context, container, "Image Not Found")
def search_image(self, context, repo, tag, exact_match): # TODO(mkrai): glance driver does not handle tags # once metadata is stored in db then handle tags LOG.debug('Searching image in glance %s', repo) try: # TODO(hongbin): find image by both repo and tag return utils.find_images(context, repo, exact_match) except Exception as e: raise exception.ZunException(six.text_type(e))