# Troubleshooting Guide

Common issues and solutions for migrations to Ansible.

## Table of Contents

- [Migration Failures](#migration-failures)
- [Conversion Issues](#conversion-issues)
- [Validation Errors](#validation-errors)
- [Deployment Problems](#deployment-problems)
- [Performance Issues](#performance-issues)
- [Chef Server Connectivity](#chef-server-connectivity)
- [Storage and State](#storage-and-state)

## Migration Failures

### Issue: Cookbook Not Found

**Symptoms:**
```
ValueError: Cookbook path does not exist: /path/to/cookbook
```

**Solutions:**

1. **Verify path is correct:**
   ```bash
   ls -la /path/to/cookbook
   ```

2. **Check cookbook structure:**
   ```bash
   # Valid cookbooks must have metadata.rb or metadata.json
   ls /path/to/cookbook/metadata.*
   ```

3. **Use absolute paths:**
   ```python
   from pathlib import Path
   cookbook_path = Path("/opt/cookbooks/myapp").resolve()
   result = orchestrator.migrate_cookbook(str(cookbook_path))
   ```

### Issue: Migration Hangs or Times Out

**Symptoms:**
- Migration process appears frozen
- No output for extended period
- Process eventually times out

**Solutions:**

1. **Check for large files:**
   ```bash
   find /path/to/cookbook -type f -size +10M
   ```
   Large template files can slow parsing. Consider excluding or optimizing them.

2. **Increase timeout (if using MCP):**
   ```json
   {
     "command": ["souschef"],
     "args": ["--timeout", "300"]
   }
   ```

3. **Enable debug logging:**
   ```python
   import logging
   logging.basicConfig(level=logging.DEBUG)

   result = orchestrator.migrate_cookbook(cookbook_path)
   ```

4. **Break into smaller chunks:**
   ```python
   # Migrate recipes individually
   from souschef.server import parse_recipe, generate_playbook_from_recipe

   for recipe in Path(cookbook_path / "recipes").glob("*.rb"):
       playbook = generate_playbook_from_recipe(str(recipe))
       # Process each playbook
   ```

### Issue: Partial Success with Warnings

**Symptoms:**
```
Status: PARTIAL_SUCCESS
Warnings: 12 resources need manual review
```

**Solutions:**

1. **Review specific warnings:**
   ```python
   result = orchestrator.migrate_cookbook(cookbook_path)

   if result.status == MigrationStatus.PARTIAL_SUCCESS:
       for warning in result.warnings:
           print(f"WARNING {warning}")
   ```

2. **Identify problematic resources:**
   ```python
   # Check metrics for manual review items
   print(f"Manual review needed: {result.metrics.resources_manual_review}")

   # Review generated playbooks for REVIEW markers
   import re
   for playbook_path in result.playbooks_generated:
       with open(playbook_path) as f:
           content = f.read()
           review_markers = re.findall(r"# REVIEW:.*", content)
           if review_markers:
               print(f"\n{playbook_path}:")
               for marker in review_markers:
                   print(f"  {marker}")
   ```

3. **Common manual review cases:**
   - Complex Ruby guards: `only_if { complex_ruby_expression }`
   - Custom resource properties not directly mappable
   - Nested attribute references
   - Dynamic resource names

## Conversion Issues

### Issue: Guards Not Converting Properly

**Symptoms:**
```
# REVIEW: manual guard conversion needed: node['platform'] == 'ubuntu' && custom_method?
when: ansible_check_mode
```

**Solutions:**

1. **Simplify Chef guards:**

   Instead of:
   ```ruby
   only_if { node['platform'] == 'ubuntu' && custom_method? }
   ```

   Split into separate checks:
   ```ruby
   only_if { node['platform'] == 'ubuntu' }
   # Handle custom_method? separately
   ```

2. **Use standard Chef idioms:**
   ```ruby
   # Good: Standard file existence
   only_if { File.exist?('/etc/nginx/nginx.conf') }

   # Problematic: Custom logic
   only_if { my_helper_method && File.readable?('/etc/app.conf') }
   ```

3. **Post-process guards manually:**
   ```python
   import yaml

   for playbook_path in result.playbooks_generated:
       with open(playbook_path) as f:
           playbook = yaml.safe_load(f)

       # Find and fix guard conditions
       for play in playbook:
           for task in play.get('tasks', []):
               if 'when' in task and 'REVIEW' in str(task['when']):
                   # Manual fix logic
                   print(f"Fix needed: {task['name']}")
   ```

### Issue: Handlers Not Generated

**Symptoms:**
- Chef `notifies` present but no Ansible handlers created
- Playbook runs but services don't restart

**Solutions:**

1. **Check notification syntax:**
   ```ruby
   # Correct
   notifies :restart, 'service[nginx]', :delayed

   # Problematic (non-standard)
   notifies :restart, resources(:service => 'nginx')
   ```

2. **Verify handler section:**
   ```bash
   # Check if handlers section exists
   grep -A 5 "^handlers:" playbook.yml
   ```

3. **Manual handler addition:**
   ```yaml
   # Add to playbook
   handlers:
     - name: Restart nginx
       ansible.builtin.service:
         name: nginx
         state: restarted
   ```

### Issue: Template Conversion Fails

**Symptoms:**
```
Error: Failed to convert template: invalid ERB syntax
```

**Solutions:**

1. **Validate ERB templates:**
   ```bash
   # Check ERB syntax
   erb -T - -P template.erb > /dev/null
   ```

2. **Fix common ERB issues:**
   ```ruby
   # Problematic: Unclosed tags
   <%= node['attribute']

   # Fixed
   <%= node['attribute'] %>
   ```

3. **Skip problematic templates:**
   ```python
   # Copy as-is and mark for manual conversion
   import shutil
   from pathlib import Path

   templates_dir = Path(cookbook_path) / "templates"
   for template in templates_dir.rglob("*.erb"):
       try:
           # Attempt conversion
           convert_template_to_jinja2(str(template))
       except Exception as e:
           print(f"Skipping {template.name}: {e}")
           # Copy for manual review
           shutil.copy(template, f"manual-review/{template.name}")
   ```

## Validation Errors

### Issue: Playbook Fails ansible-lint

**Symptoms:**
```
[701] Role info should contain platforms
[206] Variables should have spaces before and after: {{ var }}
```

**Solutions:**

1. **Auto-fix with ansible-lint:**
   ```bash
   ansible-lint --fix playbook.yml
   ```

2. **Common fixes:**

   **Missing spaces in Jinja2:**
   ```yaml
   # Before
   path: {{app_dir}}/config

   # After
   path: "{{ app_dir }}/config"
   ```

   **Quote all Jinja2 expressions:**
   ```yaml
   # Before
   when: {{ condition }}

   # After
   when: "{{ condition }}"
   ```

3. **Configure ansible-lint exceptions:**
   ```yaml
   # .ansible-lint
   skip_list:
     - '701'  # Allow missing platforms in role
     - '106'  # Allow certain variable naming
   ```

### Issue: Syntax Errors in Generated Playbooks

**Symptoms:**
```bash
$ ansible-playbook playbook.yml --syntax-check
ERROR! Syntax Error while loading YAML.
```

**Solutions:**

1. **Validate YAML:**
   ```bash
   yamllint playbook.yml
   ```

2. **Common YAML issues:**

   **Incorrect indentation:**
   ```yaml
   # Wrong
   - name: Task
   ansible.builtin.package:
     name: nginx

   # Correct
   - name: Task
     ansible.builtin.package:
       name: nginx
   ```

   **Unquoted special characters:**
   ```yaml
   # Wrong
   when: status == "running: active"

   # Correct
   when: 'status == "running: active"'
   ```

3. **Use validation in code:**
   ```python
   from souschef.core.validation import ValidationEngine

   engine = ValidationEngine()
   for playbook in result.playbooks_generated:
       engine.validate_playbook_file(playbook)

       if engine.results:
           for error in engine.results:
               print(f"{error.level}: {error.message}")
   ```

## Deployment Problems

### Issue: Cannot Connect to Ansible Platform

**Symptoms:**
```
ConnectionError: Failed to connect to https://aap.example.com
```

**Solutions:**

1. **Verify connectivity:**
   ```bash
   curl -k https://aap.example.com/api/v2/ping
   ```

2. **Check credentials:**
   ```python
   from souschef.api_clients import AAPClient

   client = AAPClient(
       server_url="https://aap.example.com",
       username="admin",
       password="password",
       verify_ssl=False,  # Only for testing!
   )

   try:
       version = client.get_api_version()
       print(f"Connected! Version: {version}")
   except Exception as e:
       print(f"Connection failed: {e}")
   ```

3. **Update SSL certificates:**
   ```bash
   # For self-signed certificates
   export REQUESTS_CA_BUNDLE=/path/to/ca-bundle.crt
   ```

### Issue: Inventory Creation Fails

**Symptoms:**
```
Failed to create inventory: 400 Bad Request
```

**Solutions:**

1. **Check inventory name:**
   ```python
   # Invalid: spaces, special characters
   inv = client.create_inventory("My App Inventory")

   # Valid: underscores, hyphens
   inv = client.create_inventory("my-app-inventory")
   ```

2. **Verify permissions:**
   ```bash
   # Check user permissions
   curl -k -u admin:password \
     https://aap.example.com/api/v2/users/self/
   ```

3. **Use organization parameter:**
   ```python
   inv = client.create_inventory(
       name="my-inventory",
       organization=1,  # Specify organization ID
   )
   ```

### Issue: Job Template Creation Fails

**Symptoms:**
```
Failed to create job template: missing required field 'inventory'
```

**Solutions:**

1. **Ensure dependencies exist:**
   ```python
   # Create in order
   inv = client.create_inventory("my-inv")
   proj = client.create_project("my-project", scm_type="git",
                                scm_url="https://github.com/org/repo")

   # Wait for project sync
   import time
   time.sleep(5)

   # Then create job template
   jt = client.create_job_template(
       name="my-job",
       inventory=inv['id'],
       project=proj['id'],
       playbook="site.yml",
   )
   ```

2. **Check required fields:**
   ```python
   jt = client.create_job_template(
       name="deploy-app",
       inventory=1,
       project=2,
       playbook="playbooks/deploy.yml",
       job_type="run",  # Required
   )
   ```

## Performance Issues

### Issue: Migration Takes Too Long

**Symptoms:**
- Large cookbooks (100+ recipes) take hours
- System becomes unresponsive

**Solutions:**

1. **Skip validation during migration:**
   ```python
   result = orchestrator.migrate_cookbook(
       cookbook_path,
       skip_validation=True,  # Validate separately
   )

   # Validate later
   from souschef.core.validation import ValidationEngine
   engine = ValidationEngine()
   for playbook in result.playbooks_generated:
       engine.validate_playbook_file(playbook)
   ```

2. **Parallelize recipe processing:**
   ```python
   from concurrent.futures import ThreadPoolExecutor
   from pathlib import Path

   recipes = list(Path(cookbook_path / "recipes").glob("*.rb"))

   def migrate_recipe(recipe_path):
       return generate_playbook_from_recipe(str(recipe_path))

   with ThreadPoolExecutor(max_workers=4) as executor:
       playbooks = list(executor.map(migrate_recipe, recipes))
   ```

3. **Use profiling:**
   ```python
   from souschef.profiling import profile_migration

   result = profile_migration(
       cookbook_path=cookbook_path,
       output_file="migration-profile.json",
   )

   # Review slow operations
   import json
   with open("migration-profile.json") as f:
       profile = json.load(f)
       # Analyse timing data
   ```

### Issue: High Memory Usage

**Symptoms:**
- Process uses >2GB RAM
- System swapping/OOM errors

**Solutions:**

1. **Process recipes individually:**
   ```python
   for recipe in Path(cookbook_path / "recipes").glob("*.rb"):
       playbook = generate_playbook_from_recipe(str(recipe))
       # Write immediately and free memory
       with open(f"playbooks/{recipe.stem}.yml", "w") as f:
           f.write(playbook)
       del playbook  # Explicit cleanup
   ```

2. **Disable caching:**
   ```python
   import os
   os.environ["SOUSCHEF_DISABLE_CACHE"] = "1"
   ```

3. **Limit concurrent operations:**
   ```python
   # Reduce parallelism
   with ThreadPoolExecutor(max_workers=2) as executor:
       # Process with fewer workers
       pass
   ```

## Chef Server Connectivity

### Issue: Authentication Failed

**Symptoms:**
```
401 Unauthorized: Authentication failed - check your Chef Server credentials
```

**Solutions:**

1. **Verify client key:**
   ```bash
   # Check key format
   head -n 1 /path/to/client.pem
   # Should be: -----BEGIN RSA PRIVATE KEY-----

   # Verify key is valid
   openssl rsa -in /path/to/client.pem -check
   ```

2. **Check client name:**
   ```python
   # Client name must match Chef Server
   client = ChefServerClient(
       server_url="https://chef.example.com",
       organization="default",
       client_name="migration-client",  # Must exist on Chef Server
       client_key=open("/etc/chef/migration.pem").read(),
   )
   ```

3. **Test connection:**
   ```python
   from souschef.core.chef_server import _validate_chef_server_connection

   success, message = _validate_chef_server_connection(
       server_url="https://chef.example.com",
       client_name="migration-client",
       organisation="default",
       client_key_path="/etc/chef/migration.pem",
   )

   if not success:
       print(f"Connection failed: {message}")
   ```

### Issue: Node Search Returns No Results

**Symptoms:**
```
nodes = chef_client.search_nodes("role:webserver")
# nodes['total'] == 0
```

**Solutions:**

1. **Verify search syntax:**
   ```python
   # Correct search queries
   nodes = chef_client.search_nodes("role:webserver")  # By role
   nodes = chef_client.search_nodes("name:app-*")     # By name pattern
   nodes = chef_client.search_nodes("recipes:apache2") # By recipe
   nodes = chef_client.search_nodes("platform:ubuntu") # By platform
   ```

2. **Check organization:**
   ```python
   # Ensure correct organization
   client = ChefServerClient(
       server_url="https://chef.example.com",
       organization="production",  # Not "default"
       client_name="migration-client",
       client_key=key_content,
   )
   ```

3. **Test with wildcard:**
   ```python
   # Get all nodes
   nodes = chef_client.search_nodes("*:*")
   print(f"Total nodes: {nodes['total']}")
   ```

## Storage and State

### Issue: Cannot Save Migration State

**Symptoms:**
```
Error: Failed to save migration state to database
```

**Solutions:**

1. **Check database connection:**
   ```python
   from souschef.storage.database import StorageManager

   storage = StorageManager(database_url="sqlite:///migrations.db")

   # Test connection
   try:
       with storage.get_session() as session:
           print("Database connected!")
   except Exception as e:
       print(f"Database error: {e}")
   ```

2. **Initialize database:**
   ```python
   # Create tables
   storage.init_db()
   ```

3. **Check permissions:**
   ```bash
   # For SQLite
   ls -la migrations.db
   chmod 664 migrations.db

   # For PostgreSQL
   psql -U user -d migrations -c "SELECT 1"
   ```

### Issue: Migration ID Not Found

**Symptoms:**
```
ValueError: Migration apache2-20260216-143022 not found
```

**Solutions:**

1. **List available migrations:**
   ```python
   # Query storage directly
   with storage.get_session() as session:
       results = session.query(MigrationState).all()
       for result in results:
           print(f"ID: {result.migration_id}, Status: {result.status}")
   ```

2. **Verify migration was saved:**
   ```python
   result = orchestrator.migrate_cookbook(cookbook_path)
   migration_id = orchestrator.save_state(result)

   # Immediately verify
   loaded = orchestrator.load_state(migration_id)
   assert loaded.migration_id == migration_id
   ```

3. **Check database file location:**
   ```bash
   # Find SQLite database
   find . -name "*.db" -type f

   # Use absolute path
   storage = StorageManager(
       database_url="sqlite:////absolute/path/to/migrations.db"
   )
   ```

## Getting Help

### Enable Debug Logging

```python
import logging

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
)

# Now run migration
result = orchestrator.migrate_cookbook(cookbook_path)
```

### Capture Detailed Error Information

```python
import traceback

try:
    result = orchestrator.migrate_cookbook(cookbook_path)
except Exception as e:
    print("="*60)
    print("DETAILED ERROR INFORMATION")
    print("="*60)
    print(f"Exception type: {type(e).__name__}")
    print(f"Exception message: {e}")
    print("\nFull traceback:")
    traceback.print_exc()
    print("="*60)
```

### Report Issues

When reporting issues, include:

1. **SousChef version:**
   ```bash
   souschef --version
   ```

2. **Environment:**
   - OS and version
   - Python version
   - Chef version being migrated
   - Target Ansible platform and version

3. **Minimal reproduction:**
   - Smallest cookbook that reproduces the issue
   - Exact command or code that fails
   - Full error message and traceback

4. **Migration result (if available):**
   ```python
   import json
   print(json.dumps(result.to_dict(), indent=2))
   ```

## See Also

- [Migration v2.0 API Reference](../api-reference/migration-v2.md)
- [Advanced Workflows](../migration-guide/advanced-workflows.md)
- [User Guide](../user-guide/examples.md)
